偶然发现，不是多严重，本地文件没有MIME，Google Chrome应该只对kSecondaryMappings中的内容进行处理，而其他未知的理应作为plain text处理，但是Chrome却对文件内容进行了嗅探，这导致任何文本文件的内容都可能会被当作脚本执行。63以后版本的Chrome已经修复了此问题。 https://bugs.chromium.org/p/chromium/issues/detail?id=777737
几个月前（http://www.nul.pw/2016/03/21/143.html）报告的漏洞，微软现在已经修复。 MS16-119 Microsoft Browser Information Disclosure Vulnerability CVE-2016-3267 Wenxiang Qian of Tencent QQBrowser 今天在seebug看到一篇paper（http://paper.seebug.org/64/），与我报的这个类似。从当时观察res相关处理逻辑的代码来看，这一块就整个是一个大坑。 鉴于这个漏洞十分简单，而且已经超过了6个月的缓和期，我就直接公开了。以下是CVE-2016-3267的细节，也是我报告时的原文（的汉译文）。 PoC by blast(Qian Wenxiang) File Exists: IE11= interactive,interactive IE10- = interactive,complete File Doesn't Exist: IE11= loading,interactive IE10- = interactive,complete so you can tell it in ie11; 漏洞影响 我注意到你们在元素中已经禁止加载res协议的URI了，在CImgHelper::SetImgCtx中有一个检查v31 = CMarkup::CheckForLMZLLoad(v8, 1)会阻止图片的加载，不过攻击者仍然可以通过Iframe来在互联网域下加载一个资源URI。 通过使用readyState属性，攻击者可以检测本地文件是否存在。例如，"interactive,interactive,"代表文件存在并且被载入了iframe，而"loading, interactive"代表文件不存在。 IE11以下的版本(IE5~10)并不受影响，IE5-10不管文件是否存在，readyState状态变化都为"interactive,complete"。也许你们（微软）应该统一IE11为低版本的策略。
从崩溃看起，IE的打印预览是如何工作的？
IE自5.5开始引入了打印预览以及自定义的打印预览功能。多亏微软的早期文档，我能从中参考到很多有用信息。 打印预览功能位于res://ieframe.dll/preview.dlg与res://ieframe.dll/preview.js中。在测试中发现雅虎日本地图在打印预览中填入666%的时候会产生崩溃。先不着急分析崩溃，让我们先看看预览和预览缩放的方式。 微软推荐使用CSS的zoom属性来为主容器设置百分比的缩放，这在preview.js中得到了良好的体现： （最后一行） function PositionPages(nDispPage) { var fRendering = false; if ((g_fCheckAutoFit || g_fCheckOrphan) && g_nDocsToCalc > 0) { UndisplayPages(); fRendering = true; } var strDispDoc = DisplayDocument(nDispPage); if (g_aDocTree != null && g_aDocTree[strDispDoc] != null && g_aDocTree[strDispDoc].Pages() > 0) { UndisplayPages(); RecalculateZoom(); MasterContainer.style.zoom = g_nZoomLevel + "%"; …… 与： function ChangeZoom() { MasterContainer.style.zoom = g_nZoomLevel + "%"; PositionPages(g_nDispPage); return g_nZoomLevel; } 在preview.dlg的LN253-256，可以看到MasterContainer，是一个DIV，这也与微软的推荐做法一致。 了解了缩放功能的作用之后，让我们再看一看页面是如何被转入预览中的。 在preview.js的CPrintDoc_AddPage()函数中，可以看到如下的代码注入到了MasterContainer的beforeEnd处。 newHTM = " <IE:DeviceRect media=\"print\" class=page id=mDiv" + this._nStatus + this._strDoc + "p" + aPage.length + ">"; newHTM += "<IE:LAYOUTRECT id=mRect" + this._nStatus + this._strDoc + "p" + aRect.length; newHTM += " class='" + classLayoutRect + "' nextRect=mRect" + this._nStatus + this._strDoc + "p" + (aRect.length + 1); newHTM += " onlayoutcomplete=\"OnRectComplete('" + this._strDoc + "', " + g_ObsoleteBar + ")\""; newHTM += " tabindex=-1 onbeforefocusenter='event.returnValue=false;' "; newHTM += " />"; newHTM += " "; newHTM += HeadFoot.HtmlHead; newHTM += " "; newHTM += " "; newHTM += HeadFoot.HtmlFoot; newHTM += " "; newHTM += "</IE:DeviceRect> "; MasterContainer.insertAdjacentHTML("beforeEnd", newHTM); 有一些特殊的元素。IE:DEVICERECT和IE:LAYOUTRECT。DeviceRect、LayoutRect两个元素用来组合展示页面。每个DeviceRect代表一个页面，LayoutRect为DeviceRect的子元素，IE在它里面显示页面预览。因为preview.dlg指定了XML Namespace（ ）所以元素前带有IE:前缀。 与其他元素不同的是，LayoutRect似乎并没有innerHTML属性。IE只是在里面展示页面的预览，页面并不能操作它。那么问题来了，IE怎么产生的预览？ 要知道这个，让我们先完整地跟踪整个流程。首先，因为打印预览是在res: protocol下的，之前的文章我们介绍了CResProtocol是处理Res Protocol的类，在DoParseAndBind上下断点可以观察到此处完整的调用： 0:058> bp MSHTML!CResProtocol::DoParseAndBind 0:牌058> g Breakpoint 0 hit eax=14061280 ebx=140611fc ecx=08101bd4 edx=1406127c esi=76c2f1fc edi=120ae270 eip=6474f8dd esp=120ae258 ebp=120ae290 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 MSHTML!CResProtocol::DoParseAndBind: 6474f8dd 8bff mov edi,edi 0:058> kvn # ChildEBP RetAddr Args to Child 00 120ae254 647504d6 14061280 14061284 14061274 MSHTML!CResProtocol::DoParseAndBind (FPO: [4,163,4]) 01 120ae268 64750495 120ae2b0 64750410 140611e0 MSHTML!CResProtocol::ParseAndBind+0x26 (FPO: [0,0,0]) 02 120ae290 76c63067 140611fc 06e8c9f0 08109f38 MSHTML!CResProtocol::Start+0x88 (FPO: [6,5,4]) 03 120ae2cc 76c30906 0810a120 08109d5c 08109f38 urlmon!COInetProt::StartEx+0x3a4 (FPO: [Non-Fpo]) 04 120ae788 76c33dfe 08109f50 08109d5c 0818ef18 urlmon!CTransaction::StartEx+0xd9e (FPO: [Non-Fpo]) 05 120ae8d0 76c33ae1 08109d5c 080beee8 6468a794 urlmon!CBinding::StartBinding+0x100c (FPO: [6,71,4]) 06 120ae920 76c33714 080beee8 00000000 6468a794 urlmon!CUrlMon::StartBinding+0x38f (FPO: [Non-Fpo]) 07 120ae94c 64678b5f 081d4de0 080beee8 00000000 urlmon!CUrlMon::BindToStorage+0x7b (FPO: [Non-Fpo]) 08 120ae980 6467a454 06e8c9f0 00000001 081d4de0 MSHTML!CTridentFilterHost::BindToMoniker+0xb9 (FPO: [Non-Fpo]) 09 120aec28 645ef090 14cb935c 00000001 00000001 MSHTML!CDwnBindData::Bind+0x9f7 (FPO: [Non-Fpo]) 0a 120aec64 645eed81 00000001 00000001 120aece0 MSHTML!NewDwnBindData+0x2be (FPO: [Non-Fpo]) 0b 120aecb8 645f20ee 14cb935c 03e7f520 00001fdd MSHTML!CDwnLoad::_InitDwnLoad+0x146 (FPO: [4,11,4]) 0c 120aece8 645f30df 03e7f520 14cb9200 120aeed0 MSHTML!CHtmLoad::InitSuper+0xf8 (FPO: [1,3,4]) 0d 120aed18 645f2e63 120aeed0 03e7f520 00000001 MSHTML!CHtmLoad::Init+0x272 (FPO: [Non-Fpo]) 0e 120aed48 64bc2a81 14cea140 00000001 00000000 MSHTML!CDwnInfo::SetLoad+0x2b6 (FPO: [Non-Fpo]) 0f 120aed74 645f2ad4 00000001 120aeed0 00000000 MSHTML!CDwnCtx::SetLoad+0x58 (FPO: [3,1,4]) 10 120aeeb4 6467517a 120aeed0 00000000 00000000 MSHTML!CMarkup::Load+0x228 (FPO: [1,71,4]) 11 120af26c 6479f0a1 120af2a0 00000000 00000000 MSHTML!CMarkup::LoadFromInfo+0xb07 (FPO: [Non-Fpo]) 12 120af3b0 6479ebf4 120af3d8 00000000 120af490 MSHTML!CDoc::LoadFromInfo+0x48d (FPO: [Non-Fpo]) 13 120af474 65075c07 03ea5400 00000001 081d4de0 MSHTML!CDoc::Load+0xd7 (FPO: [5,41,4]) 14 120af5ec 650772d6 120af678 120af668 080940c8 MSHTML!CHTMLDlg::Create+0x869 (FPO: [Non-Fpo]) 15 120af65c 6507e39d 00000000 10908ed0 00000000 MSHTML!InternalShowModalDialog+0x1c1 (FPO: [Non-Fpo]) 16 120af718 6507e54d 120af770 6629d470 0000000b MSHTML!ModelessThreadInit+0x12f (FPO: [Non-Fpo]) 17 120af764 51483a31 10908ed0 00000000 00000000 MSHTML!ModelessThreadProc+0xad (FPO: [1,13,4]) 18 120af79c 76d9337a 06f49fb0 120af7e8 77809882 IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x94 (FPO: [Non-Fpo]) 19 120af7a8 77809882 06f49fb0 43d93a90 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) 1a 120af7e8 77809855 514839a0 06f49fb0 ffffffff ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo]) 1b 120af800 00000000 514839a0 06f49fb0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo]) 可以了解到：该窗口是一个Modeless Window，那么这个调用是谁发起的呢？观察其他线程，可以发现下列调用栈： 43 Id: 5404.3b18 Suspend: 1 Teb: 7ef7e000 Unfrozen # ChildEBP RetAddr Args to Child 00 10908cc0 772d15f7 00000002 10908d10 00000001 ntdll!NtWaitForMultipleObjects+0x15 (FPO: [5,0,0]) 01 10908d5c 76d91a08 10908d10 10908d84 00000000 KERNELBASE!WaitForMultipleObjectsEx+0x100 (FPO: [Non-Fpo]) 02 10908da4 76a0086a 00000002 7efde000 00000000 kernel32!WaitForMultipleObjectsExImplementation+0xe0 (FPO: [Non-Fpo]) 03 10908df8 751b2bd1 000007f8 10908f50 ffffffff USER32!RealMsgWaitForMultipleObjectsEx+0x14d (FPO: [Non-Fpo]) 04 10908e24 751a202d 10908f50 ffffffff 10908e4c ole32!CCliModalLoop::BlockFn+0xa1 (FPO: [Non-Fpo]) (CONV: thiscall) [d:\w7rtm\com\ole32\com\dcomrem\callctrl.cxx @ 1222] 05 10908ea4 6507e08e 00000000 ffffffff 00000001 ole32!CoWaitForMultipleHandles+0xcd (FPO: [Non-Fpo]) (CONV: stdcall) [d:\w7rtm\com\ole32\com\dcomrem\sync.cxx @ 109] 06 10908f58 6507a498 1449d3c0 054b8f94 081a995c MSHTML!InternalModelessDialog+0x431 (FPO: [Non-Fpo]) 07 10908fe8 6655f9b0 00a62528 081d4de0 000003e0 MSHTML!ShowHTMLDialogEx+0xa8 (FPO: [6,32,0]) 08 109095b4 6649aafe 10909728 00000000 00000001 IEFRAME!CDocHostUIHandler::DoTemplatePrinting+0x31d (FPO: [Non-Fpo]) 09 10909688 650587fb 08260e7c 6464d428 00000007 IEFRAME!`Microsoft::WRL::Module<1,Microsoft::WRL::Details::DefaultModule<1> >::Create'::`2'::`dynamic atexit destructor for 'module''+0x64146 0a 10909b94 648662de 0a4d2b40 00000000 00000000 MSHTML!CDoc::PrintHandler+0x73a (FPO: [Non-Fpo]) 0b 1090a7a8 6464dea1 00000000 663d9884 000007d3 MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0xbdfbd 0c 1090a7c8 66562101 03ea3f00 663d9884 000007d3 MSHTML!CDoc::Exec+0x21 (FPO: [6,0,0]) 0d 1090ba08 66568f04 080fd650 00000000 1090ba70 IEFRAME!CDocHostUIHandler::ShowContextMenu+0x98d (FPO: [5,1157,4]) 0e 1090ba3c 64f4aaac 004bc898 00000000 1090ba70 IEFRAME!CDocObjectHost::ShowContextMenu+0xd4 (FPO: [Non-Fpo]) 0f 1090ba80 64f7ed1f 00000123 000000d7 00000000 MSHTML!CDoc::ShowContextMenu+0x137 (FPO: [4,7,4]) 10 1090ba9c 64f7dbe6 00000123 000000d7 00000000 MSHTML!CElement::ShowContextMenu+0x1f (FPO: [Non-Fpo]) 11 1090bc00 64eb5e79 1090bd78 00000000 64647180 MSHTML!CElement::OnContextMenu+0x17b (FPO: [2,81,4]) 12 1090bc50 64bb244c 0a58da40 1090bd78 1090bd78 MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0xb62fa 13 1090bc70 645c789e 0a58da40 1090bd78 03ea3f00 MSHTML!CElement::HandleMessage+0xc2 (FPO: [Non-Fpo]) 14 1090bc90 645c7430 14054840 0000007b 03ea3f00 MSHTML!CElement::HandleWindowMessage+0x6b (FPO: [Non-Fpo]) 15 1090bd1c 647e7558 1090bd78 14054840 00000000 MSHTML!CDoc::PumpMessage+0x638 (FPO: [3,29,4]) 16 1090be94 64cee296 0000007b 00a62528 00d70123 MSHTML!CDoc::OnMouseMessage+0x2b4 (FPO: [7,85,4]) 17 1090c058 6456c9c0 03ea3f00 0000007b 00a62528 MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0xf2357 18 1090c088 769f62fa 00a62528 0000007b 00a62528 MSHTML!CServer::WndProc+0x58 (FPO: [Non-Fpo]) 19 1090c0b4 769f6d3a 6456c960 00a62528 0000007b USER32!InternalCallWinProc+0x23 1a 1090c12c 76a00d27 00000000 6456c960 00a62528 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo]) 1b 1090c164 76a00d4d 6456c960 00a62528 0000007b USER32!CallWindowProcAorW+0xab (FPO: [Non-Fpo]) 1c 1090c184 5215c4c6 6456c960 00a62528 0000007b USER32!CallWindowProcW+0x1b (FPO: [Non-Fpo]) WARNING: Stack unwind information not available. Following frames may be wrong. 1d 1090c1fc 769f62fa 00a62528 0000007b 00a62528 TSWebMon!InitTSWebShield+0x182e6 1e 1090c228 769f6d3a 5215c278 00a62528 0000007b USER32!InternalCallWinProc+0x23 1f 1090c2a0 769f90c9 00000000 5215c278 00a62528 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo]) 20 1090c330 769f6a8c 01730ef0 00000000 00000205 USER32!RealDefWindowProcWorker+0x622 (FPO: [Non-Fpo]) 21 1090c350 769f6ae9 00a62528 00000205 00000000 USER32!RealDefWindowProcW+0x4a (FPO: [Non-Fpo]) 22 1090c398 51483218 00a62528 00000205 00000000 USER32!DefWindowProcW+0x6f (FPO: [Non-Fpo]) 23 1090c3bc 646478f7 00a62528 00000205 00000000 IEShims!NS_HangResistanceInternal::APIHook_DefWindowProcW+0x28 (FPO: [Non-Fpo]) 24 1090c3e8 64647896 00000205 00000000 002c00b8 MSHTML!CInPlace::OnDefWindowMessage+0x4d (FPO: [Non-Fpo]) 25 1090c400 6484d548 00000205 00000000 002c00b8 MSHTML!CServer::OnDefWindowMessage+0x1f (FPO: [Non-Fpo]) 26 1090c420 6484d568 1090c510 00000000 14054840 MSHTML!CDoc::TryDefWndProc+0x55 (FPO: [Non-Fpo]) 27 1090c4b4 647e7558 1090c510 14054840 00000000 MSHTML!CDoc::PumpMessage+0x841 (FPO: [3,29,4]) 28 1090c628 64fa3d66 00000205 00000000 002c00b8 MSHTML!CDoc::OnMouseMessage+0x2b4 (FPO: [7,85,4]) 29 1090c650 64ef4fa4 00000205 00000000 002c00b8 MSHTML!CMouseHandler::HandleUserInteractionMessage+0x39 (FPO: [Non-Fpo]) 2a 1090c678 645c6611 00000205 00000000 002c00b8 MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0x129c6f 2b 1090c6a4 6456ca93 00000205 00000000 002c00b8 MSHTML!CInputManager::HandleMessage+0xb7 (FPO: [Non-Fpo]) 2c 1090c864 6456c9c0 03ea3f00 00000205 00000000 MSHTML!CDoc::OnWindowMessage+0xb3 (FPO: [5,101,4]) 2d 1090c894 769f62fa 00a62528 00000205 00000000 MSHTML!CServer::WndProc+0x58 (FPO: [Non-Fpo]) 2e 1090c8c0 769f6d3a 6456c960 00a62528 00000205 USER32!InternalCallWinProc+0x23 2f 1090c938 76a00d27 00000000 6456c960 00a62528 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo]) 30 1090c970 76a00d4d 6456c960 00a62528 00000205 USER32!CallWindowProcAorW+0xab (FPO: [Non-Fpo]) 31 1090c990 5215c4c6 6456c960 00a62528 00000205 USER32!CallWindowProcW+0x1b (FPO: [Non-Fpo]) 32 1090ca08 769f62fa 00a62528 00000205 00000000 TSWebMon!InitTSWebShield+0x182e6 33 1090ca34 769f6d3a 5215c278 00a62528 00000205 USER32!InternalCallWinProc+0x23 34 1090caac 769f77c4 00000000 5215c278 00a62528 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo]) 35 1090cb0c 769f788a 5215c278 00000000 1090fce8 USER32!DispatchMessageWorker+0x3bc (FPO: [Non-Fpo]) 36 1090cb1c 6624ab8c 1090cb5c 08364580 06e4a098 USER32!DispatchMessageW+0xf (FPO: [Non-Fpo]) 37 1090fce8 6627b4e8 1090fdb4 6627b160 0835ef38 IEFRAME!CTabWindow::_TabWindowThreadProc+0x464 (FPO: [0,3181,4]) 38 1090fda8 7545d9bc 08364580 1090fdcc 6629d470 IEFRAME!LCIETab_ThreadProc+0x3e7 (FPO: [Non-Fpo]) 39 1090fdc0 51483a31 0835ef38 00000000 00000000 iertutil!CMemBlockRegistrar::_LoadProcs+0x67 (FPO: [Non-Fpo]) 3a 1090fdf8 76d9337a 0815a400 1090fe44 77809882 IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x94 (FPO: [Non-Fpo]) 3b 1090fe04 77809882 0815a400 4143333c 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) 3c 1090fe44 77809855 514839a0 0815a400 ffffffff ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo]) 3d 1090fe5c 00000000 514839a0 0815a400 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo]) 也即： 1、MSHTML!CDoc::PrintHandler处理了相关的打印请求 2、IEFRAME!CDocHostUIHandler::DoTemplatePrinting调用了MSHTML!ShowHTMLDialogEx启动了Modeless Window并加载了res://ieframe.dll/preview.dlg。 观察MSHTML!CDoc::PrintHandler。 首先CDoc::PrintHandler检查指定的CLSID是否被支持。下伪代码实际含义pCommandTarget->Exec(&CGID_DocHostCommandHandler, a10/guid/, a7, &varIn /v73/, &varOut /v67/); 开头这串比较迷，之后再看。 if ( pCommandTarget && a7 != 2 && !(*(_DWORD *)(v10 + 3584) & 0x400000) ) { hr = (*(int (__stdcall **)(int, const GUID *, int, int, int, int *))(*(_DWORD *)pCommandTarget + 16))( pCommandTarget, &CGID_DocHostCommandHandler, (a10 != 0) + 6, a7, v73, v67); v11 = hr; if ( hr != OLECMDERR_E_NOTSUPPORTED && hr != OLECMDERR_E_UNKNOWNGROUP && hr != OLECMDERR_E_DISABLED) goto Cleanup; v12 = v72; } 接着，程序寻找是否有alternative print source，也即link元素的REL=alternate MEDIA=print指定的其他可选打印源。如果没有，做一些简单的字符串处理工作： if ( !CDoc::GetAlternatePrintDoc((const unsigned __int16 *)v71, v12, (unsigned __int16 *)&v78, v12) ) { v16 = &v78; do { v17 = *(_WORD *)v16; v16 += 2; } while ( v17 != (_WORD)v69 ); //wcslen if ( (signed int)(v16 - (char *)&v79) >> 1 ) { v15 = (int)&v78; v71 = (int)&v78; LABEL_14: if ( !RatingEnabledQuery() ) { v11 = 0x80004005u; goto Cleanup; } goto LABEL_16; } } 再接下来是一个比较长的函数段。首先程序判断是否有可打印的plugin site。例如acrobat pdf都算是这种。然后发送“Print”请求。MSDN解释是“用默认模版或自定义模版”来打印（https://msdn.microsoft.com/en-us/library/aa769937(v=vs.85).aspx）。 v73 = 0; if ( !CDoc::GetPlugInSiteForPrinting(v72, &v73) ) { v18 = v73; if ( a10 ) { v11 = -2147467259; } else { memset(&v50, 0, 0x20u); v69 = 0; Cookie = 0; //发送IDM_PRINT请求 v11 = (*(int (__stdcall **)(int, GUID *, wchar_t **, signed int, LCID, ULONG_PTR *))(*(_DWORD *)v73 + 20))( v73, &GUID_NULL, &off_6449DFC0, //"Print" 1, g_lcidUserDefault, &Cookie); if ( v11 ) { FreeEXCEPINFO(v46); goto LABEL_70; } VariantInit(&pvarg); v55 = 0; v53 = 0; v54 = 0; // v11 = (*(int (__stdcall **)(int, ULONG_PTR, GUID *, LCID, signed int, char *, VARIANTARG *, char *, int *))(*(_DWORD *)v18 + 24))( v18, Cookie, &GUID_NULL, g_lcidUserDefault, 1, &v52, &pvarg, &v50, &v69); VariantClear(&pvarg); FreeEXCEPINFO(v46); } LABEL_69: ReleaseInterface((struct IUnknown *)v46); goto LABEL_70; } 然后，可以看到PrintHandler将当前页面保存到了临时目录下。 CDoc::PrintHandler(CDocument *,ushort const *,ushort const *,ulong,tagSAFEARRAY *,ulong,tagVARIANT *,tagVARIANT *,int) { ...... if ( !v71 ) { CDoc::SetTempFileTracking(1); Cookie = CDoc::HasTextSelection(v10); CDoc::SaveToTempFileForPrint( v72, &v78, 260u, Cookie != 0 ? (int)&v77 : 0, Cookie != 0 ? 0x104 : 0, Cookie != 0 ? (int)&v77 : 0); CDoc::TransferTempFileList(&v56); CDoc::SetTempFileTracking(0); v71 = (int)&v78; } ...... 随后，SetPrintCommandParameters、SetPrintManagerCommandParameters用来设置打印参数，先跳过不看，之后有一个关键操作CreateHTMLDocSource，则是负责弹出打印窗口的主人公。 v11 = CreateHTMLDocSource(v61, v39, v40, v38, (struct IInspectable **)v48, v49); CreateHTMLDocSource代码简单明了，列举如下： HRESULT __fastcall CreateHTMLDocSource(int a1, int a2, int a3, struct IWebPlatformHostSecurityManagerFactory *a4, struct IInspectable **a5, bool a6) { HRESULT v6; // esi@1 int v8; // [sp+4h] [bp-4h]@1 v8 = 0; v6 = HTMLDocumentSource::Create((int)&v8, a1, a2, (char)a4); if ( v6 >= 0 ) v6 = HTMLDocumentSource::QueryInterface(v8, &_GUID_af86e2e0_b12d_4c6a_9c5a_d7aa65101e90, a3); TSmartPointer<HTMLDocumentSource>::_TSmartPointer<HTMLDocumentSource>(&v8); return v6; } 调用HTMLDocumentSource::Create并QueryInterface并返回在参数a3中。查看HTMLDocumentSource::Create的代码，也是一个短小的函数： HRESULT __fastcall HTMLDocumentSource::Create(int a1, int a2, int a3, char a4) { int v4; // edi@1 int v5; // ebx@1 LPVOID v6; // eax@1 int v7; // esi@2 HRESULT v8; // edi@5 int v10; // [sp+Ch] [bp-4h]@4 v4 = a2; v5 = a1; v6 = HeapAlloc(g_hProcessHeap, 0, 0x60u); if ( v6 ) v7 = HTMLDocumentSource::HTMLDocumentSource(v6); else v7 = 0; v10 = v7; if ( v7 ) { v8 = HTMLDocumentSource::_Initialize((void *)v7, v4, a3, a4); if ( v8 >= 0 ) { v10 = 0; *(_DWORD *)v5 = v7; } } else { v8 = -2147024882; } TSmartPointer<HTMLDocumentSource>::_TSmartPointer<HTMLDocumentSource>(&v10); return v8; } 函数创建HTMLDocumentSource类，并调用_Initialize函数进行初始化。构造函数的代码简单易懂，全部都是赋初值的，跳过不叙述了。接下来查看_Initialize。 _Initialize是一个较长的函数，但是逻辑较为清晰，我把所有IDA没有识别出的guid等全部以注释的形式标记了。该函数先获取了IHTMLEventObj2接口，然后设置浏览模式为__IE_Immersive （在Win8下有效，会生成一个640x480的窗口，名为Immersive Mode），若Immersive Mode生效，再设置成__IE_ShrinkToFit（Shrink to fit模式，该模式下浏览器自动将页面收缩成方便打印的大小）模式。 然后，调用PrintManagerOptions::Create。这个操作将创建一个PrintManagerOptions对象，并调用其_Initialize方法，干的事情也就是拿到它的IUnkonwn接口，所以这块我们也跳过。 接下来，IE试图打开“res://ieframe.dll/preview.dlg”，很熟悉的字眼。告知该处理程序临时文件位置等设置。 HRESULT __thiscall HTMLDocumentSource::_Initialize(void *this, int a2, int a3, char a4) { int v4; // ebx@1 LPVOID *v5; // eax@1 HRESULT v6; // esi@1 int v7; // eax@2 IUnknown *v8; // esi@3 LPUNKNOWN *v9; // eax@3 int v10; // eax@4 int v11; // eax@13 int v12; // ecx@13 const WCHAR *v13; // eax@19 BSTR v14; // eax@24 LPMONIKER v15; // ecx@28 struct IMonikerVtbl *v16; // eax@29 LONG v18; // [sp-4h] [bp-130h]@23 struct HTMLDLGINFO *v19; // [sp+0h] [bp-12Ch]@24 int v20; // [sp+4h] [bp-128h]@24 int Dst; // [sp+10h] [bp-11Ch]@24 LPMONIKER v22; // [sp+14h] [bp-118h]@24 VARIANTARG *v23; // [sp+20h] [bp-10Ch]@24 char v24; // [sp+28h] [bp-104h]@26 int *v25; // [sp+2Ch] [bp-100h]@24 int v26; // [sp+30h] [bp-FCh]@24 char v27; // [sp+38h] [bp-F4h]@26 int v28; // [sp+48h] [bp-E4h]@24 int v29; // [sp+60h] [bp-CCh]@24 int v30; // [sp+70h] [bp-BCh]@24 int v31; // [sp+74h] [bp-B8h]@24 int v32; // [sp+78h] [bp-B4h]@24 int v33; // [sp+7Ch] [bp-B0h]@24 BSTR v34; // [sp+80h] [bp-ACh]@24 int v35; // [sp+84h] [bp-A8h]@24 VARIANTARG pvargSrc; // [sp+88h] [bp-A4h]@14 int v37; // [sp+98h] [bp-94h]@5 int v38; // [sp+9Ch] [bp-90h]@5 int v39; // [sp+A0h] [bp-8Ch]@5 int v40; // [sp+A4h] [bp-88h]@5 int v41; // [sp+A8h] [bp-84h]@14 int v42; // [sp+ACh] [bp-80h]@14 LPCWSTR szURL; // [sp+B0h] [bp-7Ch]@14 int v44; // [sp+B4h] [bp-78h]@14 int *v45; // [sp+B8h] [bp-74h]@13 int v46; // [sp+BCh] [bp-70h]@1 VARIANTARG pvarg; // [sp+C0h] [bp-6Ch]@8 LPUNKNOWN punkOuter; // [sp+D4h] [bp-58h]@1 void *v49; // [sp+D8h] [bp-54h]@1 LPMONIKER ppmk; // [sp+DCh] [bp-50h]@21 LONG v51; // [sp+E0h] [bp-4Ch]@4 char v52; // [sp+E7h] [bp-45h]@5 char v53; // [sp+E8h] [bp-44h]@14 unsigned int v54; // [sp+124h] [bp-8h]@1 int v55; // [sp+12Ch] [bp+0h]@1 v54 = (unsigned int)&v55 ^ __security_cookie; punkOuter = 0; v4 = (int)this; v46 = a3; v49 = this; v5 = (LPVOID *)TSmartPointer<CDCompLayer>::operator_((char *)this + 52); v6 = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, 0, 1u, &_GUID_00000146_0000_0000_c000_000000000046, v5); //guid of IGlobalInterfaceTable if ( v6 >= 0 ) { v7 = TSmartPointer<CDCompLayer>::operator_(&punkOuter); v6 = HTMLDocumentSource::QueryInterface(v4, &_GUID_00000000_0000_0000_c000_000000000046, v7); //querying IID_IUnknown if ( v6 >= 0 ) { v8 = punkOuter; v9 = (LPUNKNOWN *)TSmartPointer<CDCompLayer>::operator_(v4 + 56); v6 = CoCreateFreeThreadedMarshaler(v8, v9); if ( v6 >= 0 ) { v51 = 0; v10 = TSmartPointer<IDispEffectConvolveMatrix>::operator_(&v51); v6 = (**(int (__stdcall ***)(int, GUID *, int))a2)(a2, &_GUID_3050f48b_98b5_11cf_bb82_00aa00bdce0b, v10); //getting IHTMLEventObj2 if ( v6 >= 0 ) { v52 = 1; v37 = 0; v38 = 0; v39 = 0; v40 = 0; if ( (*(int (__stdcall **)(LONG, _DWORD, _DWORD, int *))(*(_DWORD *)v51 + 32))( v51, L"__IE_Immersive", 0, &v37) >= 0 && (_WORD)v37 == 11 && -1 == (_WORD)v39 ) { *(_QWORD *)&pvarg.vt = 0i64; *(_QWORD *)&pvarg.lVal = 0i64; if ( (*(int (__stdcall **)(LONG, _DWORD, _DWORD, VARIANTARG *))(*(_DWORD *)v51 + 32))( v51, L"__IE_ShrinkToFit", 0, &pvarg) < 0 || pvarg.vt != 11 || (v52 = 1, -1 != LOWORD(pvarg.lVal)) ) v52 = 0; VariantClear(&pvarg); } v45 = (int *)((char *)v49 + 60); v11 = TSmartPointer<PrintManagerOptions>::operator_((char *)v49 + 60); LOBYTE(v12) = v52; v6 = PrintManagerOptions::Create(v12, v11); if ( v6 >= 0 ) { v41 = 0; v42 = 0; szURL = 0; v44 = 0; memcpy(&v53, L"res://ieframe.dll/preview.dlg", 0x3Cu); *(_QWORD *)&pvargSrc.vt = 0i64; *(_QWORD *)&pvargSrc.lVal = 0i64; if ( (*(int (__stdcall **)(LONG, _DWORD, _DWORD, VARIANTARG *))(*(_DWORD *)v51 + 32))( v51, L"__IE_TemporaryFiles", 0, &pvargSrc) >= 0 && pvargSrc.vt == 8200 ) VariantCopy((VARIANTARG *)((char *)v49 + 72), &pvargSrc); if ( (*(int (__stdcall **)(_DWORD, _DWORD, _DWORD, int *))(*(_DWORD *)v51 + 32))( v51, L"__IE_TemplateUrl", 0, &v41) < 0 || (_WORD)v41 != 8 || (v13 = szURL) == 0 ) v13 = (const WCHAR *)&v53; ppmk = 0; v6 = CreateURLMonikerEx(0, v13, &ppmk, 1u); if ( v6 >= 0 ) { *(_QWORD *)&pvarg.vt = 0i64; *(_QWORD *)&pvarg.lVal = 0i64; pvarg.vt = 13; v6 = PrintManagerOptions::QueryInterface( *v45, &_GUID_00000000_0000_0000_c000_000000000046, (int)&pvarg.lVal); if ( v6 >= 0 ) { v18 = 0; v6 = (*(int (__stdcall **)(LONG, _DWORD, _DWORD, _DWORD, LONG, _DWORD, _DWORD))(*(_DWORD *)v51 + 28))( v51, L"__PE_PrintManagerOptions", *(_DWORD *)&pvarg, *(_DWORD *)&pvarg.wReserved2, pvarg.lVal, HIDWORD(pvarg.dblVal), 0); if ( v6 >= 0 ) { VariantClear(&pvarg); pvarg.vt = 13; pvarg.lVal = v51; v18 = v51; (*(void (__stdcall **)(LONG))(*(_DWORD *)v51 + 4))(v51); HTMLDLGINFO::HTMLDLGINFO(&Dst); v32 = 0; v33 = 0; v34 = 0; v35 = 0; LOWORD(v32) = 8; v14 = SysAllocString(0); Dst = 0; v29 = 0; v34 = v14; v22 = ppmk; v23 = &pvarg; v25 = &v32; v28 = 720; v26 = 1; v30 = 1; v31 = v46; v6 = InternalModelessDialog(v19, v20); if ( v6 >= 0 ) *((_BYTE *)v49 + 88) = a4; VariantClear((VARIANTARG *)&v32); VariantClear((VARIANTARG *)&v27); CStr::_Free(&v24); } } VariantClear(&pvarg); } v15 = ppmk; ppmk = 0; if ( v15 ) { v16 = v15->lpVtbl; v18 = (LONG)v15; v16->Release(v15); } VariantClear(&pvargSrc); VariantClear((VARIANTARG *)&v41); } VariantClear((VARIANTARG *)&v37); } TSmartPointer<IMFGetService>::_TSmartPointer<IMFGetService>(&v51); } } } TSmartPointer<IMFGetService>::_TSmartPointer<IMFGetService>(&punkOuter); return v6; } 最后，通过InternalModelessDialog启动该窗口实现预览 HRESULT __usercall InternalModelessDialog(int a1, int a2, HRESULT a3) 那么这个预览窗口中显示的文档和其他的到底有什么不同呢？根据MSDN文档的意思是：有点像禁止了Script的WebBrowser。代码将Layout当作一个容器来显示预览后的内容。在Modeless Window中，我们不能F12，不能呼出右键菜单，怎么办？我们有另一种方法——通过IE暴露的接口来取得整个窗口的DOM内容。 这个逻辑中，预览的窗口标题为“打印预览”。为了方便，这里就直接Hardcode写上“打印预览”四个字了。首先，使用FindWindow找到对应的窗口并枚举出正确的Server窗口，或是使用Spy++找到窗口的HWND（因为这里我们并不是写一个通用的工具，所以怎么方便怎么来）。 注意目标窗口是Internet Explorer_Server而不是外层的Internet Explorer_TridentDlgFrame。这里使用的代码如下： // prjFindPrintview.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <mshtml.h> #include <Exdisp.h> #include <atlbase.h> #include <SHLGUID.h> #include <oleacc.h> #include <comdef.h> #include <tchar.h> #pragma comment(lib, "Oleacc.lib") int _tmain(int argc, _TCHAR* argv[]) { ::CoInitialize(NULL); HRESULT hr = S_OK; UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT")); LRESULT lRes = 0; ::SendMessageTimeout((HWND)0x000711FA, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD)&lRes); CComPtr<IHTMLDocument2> spDoc; hr = ObjectFromLresult(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc); if (FAILED(hr)) return -1; CComPtr<IHTMLElementCollection> spElementCollection; hr = spDoc->get_all(&spElementCollection); if (FAILED(hr)) return -2; long lElementCount; hr = spElementCollection->get_length(&lElementCount); if (FAILED(hr)) return -3; VARIANT vIndex; vIndex.vt = VT_I4; VARIANT vSubIndex; vSubIndex.vt = VT_I4; vSubIndex.lVal = 0; for (vIndex.lVal = 0; vIndex.lVal < lElementCount; vIndex.lVal++) { CComPtr<IDispatch> spDispatchElement; if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement))) continue; CComPtr<IHTMLElement> spElement; if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement))) continue; CComBSTR outerHTML; spElement->get_outerHTML(&outerHTML); OutputDebugStringW(outerHTML); } ::CoUninitialize(); return 0; } 取得窗口的DOM如下： 将其输出到文件，自行添上 即为完整的内容。不过需要记得的是Layout等元素只在打印预览中有效，所以单独提取出来崩溃不了也不要太疑惑，这里只是为了方便了解预览中最终的DOM分布情况而已。 接下来，让我们复现崩溃吧。打开map.yahoo.co.jp，访问出现崩溃的代码，右键预览。只不过这次我们使用缓存文件的方式来精简代码。预览后，从%TEMP%下复制走所有新生成的HTM文件，然后调节打印预览的比例为666%。在确定仍然可以崩溃之后，让我们开始删除无用数据，记下异常偏移e9b102（在我的机器上是这个数字，因为有些崩溃在精简代码时可能变成其他的崩溃，所以最好记下偏移地址，以免中途不知道从什么位置产生了新问题）。多次精简之后，我们留下了下列代码： 而且精简到这一步就可以发现，只要发起打印预览，IE就会直接崩溃。所以，为了让IE自动触发崩溃，我们手写一下调用print();即可。 跟随调试可以发现，崩溃位置维持不变： 0:040> g (24c8.2960): Access violation - code c0000005 (!!! second chance !!!) eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=045c9220 edi=0a68eeac eip=6537b102 esp=0a68eea4 ebp=0a68eeb8 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 MSHTML!CDispParentNode::ComputeVisibleBoundsOnDirtySVGSubtree+0x16: 6537b102 8b33 mov esi,dword ptr [ebx] ds:002b:00000000=???????? 接下来让我们看看打印预览窗口的DOM结构。因为DIV的STYLE属性是触发崩溃的关键条件之一，但是我们又想拿到预览窗口的DOM，所以我们得做一些改动，先手动删除部分STYLE，然后预览，这时，便可使用我们之前的程序抓出DOM。 篇幅考虑我就不扯淡再去看这个DOM了，直接以上述精简后的代码开始分析工作。 在崩溃后查看崩溃栈如下： 0:033> kvn # ChildEBP RetAddr Args to Child 00 0b2df530 64c7b6b7 0b2df75c 0b2df75c 64c7b63a MSHTML!CDispParentNode::ComputeVisibleBoundsOnDirtySVGSubtree+0x16 (FPO: [Non-Fpo]) 01 0b2df774 645de05b 0b2df7bc 0b2df79a 0b2dfb3c MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0x2b2eb 02 0b2df860 64b4110c 0b2dfb3c 0b2dfb3c 0b2dfb3c MSHTML!Layout::FlowBoxBuilder::BuildBoxItem+0x89 (FPO: [1,51,4]) 03 0b2df87c 64b410d7 0b2dfb3c 0d82d0e0 0b2df8e4 MSHTML!Layout::LayoutBuilder::BuildBoxItem+0x2e (FPO: [Non-Fpo]) 04 0b2df88c 64b40193 0b2dfb3c 0b2df908 64e657d0 MSHTML!Layout::LayoutBuilder::Move+0x57 (FPO: [Non-Fpo]) 05 0b2df8e4 65358d8d 0d83264c 00000000 0d83264c MSHTML!Layout::LayoutBuilderDriver::BuildElementLayout+0xce (FPO: [Non-Fpo]) 06 0b2df974 65359137 ffffffff 00000001 04c29ab4 MSHTML!Layout::MultiFragmentBoxBuilder::BuildCrossFragmentPositionedElement+0x1c3 (FPO: [Non-Fpo]) 07 0b2df9d4 64ec4753 0b2dfa24 0d82d020 0d82cff0 MSHTML!Layout::MultiFragmentBoxBuilder::PositionAndArrangeCrossFragmentAbsolutePositionedElement+0x17e (FPO: [Non-Fpo]) 08 0b2dfa2c 645d06b0 0d7e8ba0 04c29ab4 0b2dfb3c MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0xce2e3 09 0b2dfb14 645d72b9 0b2dfb3c 0b2dfb48 645d6ae0 MSHTML!Layout::PageCollection::LayoutPagesCore+0x37e (FPO: [Non-Fpo]) 0a 0b2dfb40 65340900 65340760 0b2dfb88 0d832648 MSHTML!Layout::PageCollection::LayoutPages+0xca (FPO: [Non-Fpo]) 0b 0b2dfb7c 64a9ec7e 00100000 04c29500 00000000 MSHTML!Layout::PageCollection::DoLayout+0x1a0 (FPO: [1,9,4]) 0c 0b2dfbcc 64592161 00100000 64a7d7f0 00000000 MSHTML!CView::ExecuteLayoutTasks+0x159 (FPO: [Non-Fpo]) 0d 0b2dfc24 64a7d840 00000000 64a7d7f0 0b2dfc54 MSHTML!CView::EnsureView+0x3bb (FPO: [1,15,4]) 0e 0b2dfc44 64585813 04c29b38 00000000 00000001 MSHTML!CView::EnsureViewCallback+0x50 (FPO: [Non-Fpo]) 0f 0b2dfc8c 6456d52c 3f72ceb5 00000000 6456cc90 MSHTML!GlobalWndOnMethodCall+0x17b (FPO: [Non-Fpo]) 10 0b2dfce0 769f62fa 000825e0 00008002 00000000 MSHTML!GlobalWndProc+0x103 (FPO: [Non-Fpo]) 11 0b2dfd0c 769f6d3a 6456cc90 000825e0 00008002 USER32!InternalCallWinProc+0x23 12 0b2dfd84 769f77c4 0539315c 6456cc90 000825e0 USER32!UserCallWinProcCheckWow+0x109 (FPO: [Non-Fpo]) 13 0b2dfde4 769f788a 6456cc90 00000000 0b2dfe40 USER32!DispatchMessageWorker+0x3bc (FPO: [Non-Fpo]) 14 0b2dfdf4 6507e640 0b2dfe24 0b2dfe4c 6629d470 USER32!DispatchMessageW+0xf (FPO: [Non-Fpo]) 15 0b2dfe40 6ed93a31 05c7ab90 00000000 00000000 MSHTML!ModelessThreadProc+0x1a0 (FPO: [1,13,4]) MSHTML!CDispParentNode::ComputeVisibleBoundsOnDirtySVGSubtree是一个Thiscall，代码十分简单，就是调用自己一个成员函数： int __thiscall CDispParentNode::ComputeVisibleBoundsOnDirtySVGSubtree(void *this, int a2, int a3, int a4, float a5) { return (*(int (__stdcall **)(_DWORD, int, _DWORD, _DWORD, _DWORD, _DWORD))(*(_DWORD *)this + 216))( 0, a3, 0, 0, LODWORD(a5), 0); } 查看函数代码，很明显，this指针为null。 0:033> uf . MSHTML!CDispParentNode::ComputeVisibleBoundsOnDirtySVGSubtree: 6537b0ec 8bff mov edi,edi 6537b0ee 55 push ebp 6537b0ef 8bec mov ebp,esp 6537b0f1 d94514 fld dword ptr [ebp+14h] 6537b0f4 33c0 xor eax,eax 6537b0f6 53 push ebx 6537b0f7 56 push esi 6537b0f8 57 push edi 6537b0f9 8bfc mov edi,esp 6537b0fb 8bd9 mov ebx,ecx ; ebx = ecx = this 6537b0fd 50 push eax 6537b0fe 51 push ecx 6537b0ff d91c24 fstp dword ptr [esp] 6537b102 8b33 mov esi,dword ptr [ebx] ; esi = *ebx 查看上一层 0:033> ub . MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0x2b2d4: 64c7b6a0 83ec08 sub esp,8 64c7b6a3 d95c2404 fstp dword ptr [esp+4] 64c7b6a7 51 push ecx 64c7b6a8 51 push ecx 64c7b6a9 8b08 mov ecx,dword ptr [eax] 64c7b6ab e8307699ff call MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent (64612ce0) 64c7b6b0 8bc8 mov ecx,eax 64c7b6b2 e835fa6f00 call MSHTML!CDispParentNode::ComputeVisibleBoundsOnDirtySVGSubtree (6537b0ec) 很明显MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent返回了NULL。 int __thiscall Layout::LayoutBox::GetDisplayNodeAsParent(void *this) { int pThisVar; // eax@1 int nResult; // edx@1 int v3; // esi@2 int v4; // edi@2 int v5; // eax@5 pThisVar = (*(int (**)(void))(*(_DWORD *)this + 788))(); nResult = pThisVar; if ( !pThisVar ) goto Cleanup; v3 = *(_DWORD *)(pThisVar + 28); v4 = pThisVar; if ( *(_BYTE *)(v3 + 56) & 1 && !*(_BYTE *)(*(_DWORD *)(__readfsdword(44) + 4 * LODWORD(_tls_index)) + 36) && *(_DWORD *)(v3 + 4) != *(_DWORD *)(pThisVar + 8) ) { v5 = *(_DWORD *)(pThisVar + 4); if ( !v5 ) v5 = nResult; if ( v5 != nResult ) v4 = v5; } if ( !(*(_BYTE *)(v4 + 21) & 0x20) ) { Cleanup: nResult = NULL; } return nResult; } 可以简单跟踪一下代码看看到底是哪儿出现了问题。为了避免重复断到断点上（Layout的调用是十分频繁的），可以 bp MSHTML!ModelessThreadProc 触发后 bc 0 bp MSHTML!Layout::FlowBoxBuilder::BuildBoxItem 触发后 bc 0 bp MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent 这样就没问题了 0:031> g Breakpoint 1 hit eax=05618f80 ebx=0c07f060 ecx=0eebc4e0 edx=00000002 esi=0ee946e0 edi=0ee5ecb0 eip=64612ce0 esp=0c07f04c ebp=0c07f28c iopl=0 nv up ei pl nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212 MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent: 64612ce0 8bff mov edi,edi 0:031> k ChildEBP RetAddr 0c07f048 64c7b6b0 MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent 0c07f28c 645de05b MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0x2b2e4 0c07f378 64b4110c MSHTML!Layout::FlowBoxBuilder::BuildBoxItem+0x89 0c07f394 64b410d7 MSHTML!Layout::LayoutBuilder::BuildBoxItem+0x2e 0c07f3a4 64b40193 MSHTML!Layout::LayoutBuilder::Move+0x57 0c07f3fc 65358d8d MSHTML!Layout::LayoutBuilderDriver::BuildElementLayout+0xce 再度跟踪可以发现是 pThisVar = (*(int (**)(void))(*(_DWORD *)this + 788))();// MSHTML!Layout::SvgBox::GetDisplayNode //returns null nResult = pThisVar; if ( !pThisVar ) goto Cleanup; //return NULL(0); 查看GetDisplayNode，很明显，校验失败，返回了0. int __thiscall Layout::SvgBox::GetDisplayNode(int this) { int result; // eax@2 if ( !(*(_BYTE *)(this + 56) & 4) || *(_BYTE *)(*(_DWORD *)(__readfsdword(44) + 4 * LODWORD(_tls_index)) + 36) ) result = *(_DWORD *)(this + 16); else result = 0; return result; } 加上对内存的跟踪，我们可以断定这是一个寻常的空指针问题。
有一个基于ie 10的use after free，可是vtguard是个麻烦事儿。算了留待周末再看吧。 看起来是一个从ie8就遗留下来的老问题了 20150609 add 但是代码整理起来相当麻烦，看起来还和运行过程中多线程的时序有关，整体崩溃成功率大致在60%左右，五次有三次成功的样子。 本来想尽快整理完出结果，但是精简过程中就出现了时序被破坏之后的各种不稳定情况，很多时候直接变成了无用的空指针解引用，看来这个东西不闹腾个几个月是出不来了，只求这之前还没人报告给微软了 20150630 add 有了点时间 调整了下 目前触发率提升到了100% 无关的参考链接 http://neilscomputerblog.blogspot.com/2014/04/vtguard.html http://ifsec.blogspot.com/2013/11/exploiting-internet-explorer-11-64-bit.html
看看AxMan的源代码是怎么获取COM组件的信息的
主要看AxMan枚举所有ActiveX的属性的地方。 是个坑，但是偶尔也会填填。 1、如何确定机器上有哪些ActiveX已经装上了？ 很明智的做法： RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Classes\\CLSID"), 0, KEY_READ, &hKey); RegQueryInfoKey( hKey, // key handle NULL, // buffer for class name NULL, // size of class string NULL, // reserved &cSubKeys, // number of subkeys NULL, // longest subkey size NULL, // longest class string NULL, // number of values for this key NULL, // longest value name NULL, // longest value data NULL, // security descriptor NULL // last write time ); for (j = 0; j<cSubKeys; j++) { cbName=MAX_PATH; achKey[0] = '\0'; RegEnumKeyEx(hKey, j, achKey, &cbName, NULL, NULL, NULL, &ftime); _ftprintf(output, _T("\t'%s'%s\n"), achKey, j == (cSubKeys-1) ? _T("") : _T(",")); fflush(output); scan_clsid(achKey); } 针对找到的每个CLSID主键都调用一次“自己 GO {CLSID}”，很明智的递归方式，因为我发现在枚举的时候它就崩了无数次了。搁在一个进程里面估计这程序就完了。 2、看看GO CLSID的时候它做了啥： （a）先设置一个异常处理程序，以便出错的时候做报告。 虽然我一次都没见它报告过。 （b）调用CLSIDFromString将传来的CLSID转为CLSID类型，简单来说就是转成这样： typedef struct { unsigned long Data1; unsigned short Data2; unsigned short Data3; byte Data4[ 8 ]; } GUID; （c）读取HKLM\SOFTWARE\CLASSES\CLSID{SOME-CLSID}\的默认值，通常它像是： HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{00000000-12C9-4305-82F9-43058F20E8D2} (默认) QQDownload IE Left Helper REG_SZ 是的，这里有描述。 （d）再读HKLM\SOFTWARE\CLASSES\CLSID{SOME-CLSID}\ProgId的默认值，通常它的值像是： 还是上面的例子， HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{00000000-12C9-4305-82F9-43058F20E8D2}\ProgID (默认) QQIEHelper.QQCycloneHelper.1 REG_SZ 对，就是ProgId，字面意思。 （e）再读HKLM\SOFTWARE\CLASSES\CLSID{SOME-CLSID}\InProc32的默认值，通常它就是要加载的DLL，至于ThreadingModel，大家不是都默认Both或者Apartment了吗，它也没管。 （f）然后就到了最重要的部分，它怎么枚举属性和方法？ [I] 先CoCreateInstance创建一个实例，查询其IObjectSecurity接口； [II] 如果实现了这个接口，查询是否设置了Safe for init和Safe for script位，这个是它待会儿要写到测试的配置里面去的； [III] 调用IDispatch中的GetTypeInfo获取ITypeInfo接口用来展开相关的内容； The ITypeInfo interface provides access to the following: The set of function descriptions associated with the type. For interfaces, this contains the set of member functions in the interface. The set of data member descriptions associated with the type. For structures, this contains the set of members of the type. The general attributes of the type, such as whether it describes a structure, an interface, and so on. 简单的说就是ITypeInfo接口提供了对这个接口中的成员函数、成员变量、通用属性（是否定义了一个结构体，一个接口等等）。 [IV] 对TypeInfo调用GetDocumentation获取函数数量，然后对各个函数调用GetFuncDesc获取函数描述，然后这里就可以获得函数名，返回值，参数数量和参数。
不下两分钟已经弄崩四五次了，出现这么个栈调用，大家也都知道是什么情况了吧： 0:000> kvn # ChildEBP RetAddr Args to Child 00 00128688 7c92de5c 7c801e3a ffffffff c0000409 ntdll!KiFastSystemCallRet (FPO: [0,0,0]) 01 0012868c 7c801e3a ffffffff c0000409 001289d4 ntdll!ZwTerminateProcess+0xc (FPO: [2,0,0]) 02 0012869c 004c9453 ffffffff c0000409 310a3291 kernel32!TerminateProcess+0x20 (FPO: [Non-Fpo]) WARNING: Stack unwind information not available. Following frames may be wrong. 03 001289d4 004712e9 0000011e 0324028a 00000109 360zip+0xc9453
打开wps就崩溃，看看啥原因呗，为了防止被它的异常上报程序捕获，首先先挂上调试器 (1e60.1a6c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** ERROR: Symbol file could not be found. Defaulted to export symbols for D:\Program Files (x86)\Kingsoft\WPS Office\9.1.0.4867\office6\officespace.DLL - eax=00000000 ebx=00000011 ecx=0036ee80 edx=003428c3 esi=776a8ad0 edi=00000000 eip=7748f3c4 esp=09b3fd00 ebp=09b3fd0c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 officespace!KActiveLoginInfoCache::vipUrl+0x9e8: 7748f3c4 8b480c mov ecx,dword ptr [eax+0Ch] ds:002b:0000000c=???????? 看看栈 0:017> kvn # ChildEBP RetAddr Args to Child WARNING: Stack unwind information not available. Following frames may be wrong. 00 09b3fd0c 7749235b acc53f64 00000000 09b3fd8c officespace!KActiveLoginInfoCache::vipUrl+0x9e8 01 09b3fd40 77491f93 09b3fd5c acc53f74 06b50300 officespace!KActiveLoginInfoCache::vipUrl+0x397f 02 09b3fd74 774996cf acc53f9c 06b50220 06b50300 officespace!KActiveLoginInfoCache::vipUrl+0x35b7 *** ERROR: Symbol file could not be found. Defaulted to export symbols for D:\Program Files (x86)\Kingsoft\WPS Office\9.1.0.4867\office6\QtCore4.dll - 03 09b3fdb8 668fb948 00000000 00000000 09b3fe00 officespace!KActiveLoginInfoCache::vipUrl+0xacf3 04 09b3fdc8 57b7c556 06b50300 acb26a30 00000000 QtCore4!QThread::setPriority+0x25d 05 09b3fe00 57b7c600 00000000 09b3fe18 76a0338a MSVCR100!_endthreadex+0x3f (FPO: [Non-Fpo]) 06 09b3fe0c 76a0338a 06cc4178 09b3fe58 771c9f72 MSVCR100!_endthreadex+0xce (FPO: [Non-Fpo]) 07 09b3fe18 771c9f72 06cc4178 2660fcd4 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) 08 09b3fe58 771c9f45 57b7c59c 06cc4178 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo]) 09 09b3fe70 00000000 57b7c59c 06cc4178 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo]) 看看出错的东西是哪儿来的吧 7748f3a7 55 push ebp 7748f3a8 8bec mov ebp,esp 7748f3aa 51 push ecx 7748f3ab 53 push ebx 7748f3ac 57 push edi 7748f3ad 8d45fc lea eax,[ebp-4] 7748f3b0 50 push eax 7748f3b1 ff1518e45b77 call dword ptr [officespace!KQingClient::trUtf8+0x7a638 (775be418)] 7748f3b7 8d4dfc lea ecx,[ebp-4] 7748f3ba ff151ce45b77 call dword ptr [officespace!KQingClient::trUtf8+0x7a63c (775be41c)] 7748f3c0 8bd8 mov ebx,eax 7748f3c2 8b06 mov eax,dword ptr [esi] 0:017> ?(.-7748f3a7) Evaluate expression: 29 = 0000001d 0:017> r eax=00000000 ebx=00000011 ecx=0036ee80 edx=003428c3 esi=776a8ad0 edi=00000000 eip=7748f3c4 esp=09b3fd00 ebp=09b3fd0c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 officespace!KActiveLoginInfoCache::vipUrl+0x9e8: 7748f3c4 8b480c mov ecx,dword ptr [eax+0Ch] ds:002b:0000000c=???????? 0:017> ?(0x9e8-0x1d) Evaluate expression: 2507 = 000009cb 0:017> x officespace!KActiveLoginInfoCache::vipUrl+000009cb 那就是这里了，断下重启， Breakpoint 0 hit eax=0b1cf83c ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3a7 esp=0b1cf818 ebp=0b1cf848 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9cb: 05daf3a7 55 push ebp 0:017> dd esi 05fc8ad0 00000000 00000001 05f5c5e4 06fefc48 05fc8ae0 00000000 00000000 00000000 66d13d90 05fc8af0 00000000 66d13d90 66d13d90 00000000 05fc8b00 66d13d90 66d13d90 00000000 66d13d90 05fc8b10 66d13d90 66d13d90 66d13d90 66d13d90 05fc8b20 00000000 66d13d90 66d13d90 66d13d90 05fc8b30 66d11874 00000000 66d13d90 00000000 05fc8b40 66d13d90 66d13d90 00000000 00000001 0:017> p eax=0b1cf83c ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3a8 esp=0b1cf814 ebp=0b1cf848 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9cc: 05daf3a8 8bec mov ebp,esp 0:017> eax=0b1cf83c ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3aa esp=0b1cf814 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9ce: 05daf3aa 51 push ecx 0:017> eax=0b1cf83c ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3ab esp=0b1cf810 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9cf: 05daf3ab 53 push ebx 0:017> eax=0b1cf83c ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3ac esp=0b1cf80c ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9d0: 05daf3ac 57 push edi 0:017> eax=0b1cf83c ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3ad esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9d1: 05daf3ad 8d45fc lea eax,[ebp-4] 0:017> eax=0b1cf810 ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3b0 esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9d4: 05daf3b0 50 push eax 0:017> eax=0b1cf810 ebx=00000000 ecx=0b1cf864 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3b1 esp=0b1cf804 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9d5: *** ERROR: Symbol file could not be found. Defaulted to export symbols for D:\Program Files (x86)\Kingsoft\WPS Office\9.1.0.4867\office6\QtCore4.dll - 05daf3b1 ff1518e4ed05 call dword ptr [officespace!KQingClient::trUtf8+0x7a638 (05ede418)] ds:002b:05ede418={QtCore4!QDateTime::time (665bffde)} 0:017> eax=0b1cf810 ebx=00000000 ecx=03ddee82 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3b7 esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9db: 05daf3b7 8d4dfc lea ecx,[ebp-4] 0:017> eax=0b1cf810 ebx=00000000 ecx=0b1cf810 edx=00d40174 esi=05fc8ad0 edi=00000000 eip=05daf3ba esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 officespace!KActiveLoginInfoCache::vipUrl+0x9de: 05daf3ba ff151ce4ed05 call dword ptr [officespace!KQingClient::trUtf8+0x7a63c (05ede41c)] ds:002b:05ede41c={QtCore4!QTime::hour (665bf3d9)} 0:017> eax=00000012 ebx=00000000 ecx=0036ee80 edx=00012982 esi=05fc8ad0 edi=00000000 eip=05daf3c0 esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 officespace!KActiveLoginInfoCache::vipUrl+0x9e4: 05daf3c0 8bd8 mov ebx,eax 0:017> eax=00000012 ebx=00000012 ecx=0036ee80 edx=00012982 esi=05fc8ad0 edi=00000000 eip=05daf3c2 esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 officespace!KActiveLoginInfoCache::vipUrl+0x9e6: 05daf3c2 8b06 mov eax,dword ptr [esi] ds:002b:05fc8ad0=00000000 0:017> eax=00000000 ebx=00000012 ecx=0036ee80 edx=00012982 esi=05fc8ad0 edi=00000000 eip=05daf3c4 esp=0b1cf808 ebp=0b1cf814 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 officespace!KActiveLoginInfoCache::vipUrl+0x9e8: 05daf3c4 8b480c mov ecx,dword ptr [eax+0Ch] ds:002b:0000000c=???????? 看看这个值哪儿来的？ 05db22b1 bed08afc05 mov esi,offset officespace!KxKuaiPanInfoGetter::staticMetaObject+0x5998 (05fc8ad0) 0:017> dd 05fc8ad0 05fc8ad0 00000000 果然是空指针，往上追溯： 05db22ff 8b0d74ffed05 mov ecx,dword ptr [officespace!KQingClient::trUtf8+0x7c194 (05edff74)] ... 05db2307 890dd08afc05 mov dword ptr [officespace!KxKuaiPanInfoGetter::staticMetaObject+0x5998 (05fc8ad0)],ecx 在officespace里面，那么重启，加ba r1： 0:000> sxe ld officespace 0:000> g ModLoad: 0f3e0000 0f63b000 D:\Program Files (x86)\Kingsoft\WPS Office\9.1.0.4867\office6\officespace.DLL eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=7efdd000 edi=0032edf4 eip=771afc62 esp=0032ecc8 ebp=0032ed1c 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!ZwMapViewOfSection+0x12: 771afc62 83c404 add esp,4 0:000> x officespace!KQingClient::trUtf8 *** ERROR: Symbol file could not be found. Defaulted to export symbols for D:\Program Files (x86)\Kingsoft\WPS Office\9.1.0.4867\office6\officespace.DLL - 0f4a3d9b officespace!KQingClient::trUtf8 (<no parameter info>) 0f4a3de0 officespace!KQingClient::trUtf8 (<no parameter info>) 0:000> ba r1 0f4a3d9b+0x7c194 "r;k;g;" 0:000> ba r1 0f4a3de0+0x7c194 "r;k;g;" 0:000> g 结果，大家都知道是为啥了： eax=66a81874 ebx=00000000 ecx=00000001 edx=00000471 esi=075d42e8 edi=00000000 eip=0f3eb76d esp=0032d188 ebp=0032d1a4 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206 officespace!KxKuaiPanInfoGetter::qt_metacall+0x6483: 0f3eb76d 894610 mov dword ptr [esi+10h],eax ds:002b:075d42f8=baadf00d 0:017> ln 66a81874 (66a81874) QtCore4!QListData::shared_null | (66a81a40) QtCore4!QMapData::shared_null Exact matches: QtCore4!QListData::shared_null (<no parameter info>) SetContext failed, 0x80070005 MachineInfo::SetContext failed - Thread: 04CE8F30 Handle: 840 Id: 4174 - Error == 0x80070005 eax=66a81874 ebx=00000001 ecx=00006379 edx=00006378 esi=66536c1f edi=0f608a18 eip=0f44681f esp=0032d1e0 ebp=0032d204 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202 officespace!qt_test_isFetchedRoot+0x1d040: 0f44681f a3548a600f mov dword ptr [officespace!KxKuaiPanInfoGetter::staticMetaObject+0x591c (0f608a54)],eax ds:002b:0f608a54=00000000 ChildEBP RetAddr WARNING: Stack unwind information not available. Following frames may be wrong. 0032d204 0f446765 officespace!qt_test_isFetchedRoot+0x1d040 0032d228 0f48a913 officespace!qt_test_isFetchedRoot+0x1cf86 0032d260 0f3e75f2 officespace!verify+0x18999 0032d26c 664d5bce officespace!KxKuaiPanInfoGetter::qt_metacall+0x2308
先埋坑，旧的那些IE的坑填完了再写这篇好了。 漏洞作者：blast 来源：http://nul.pw/ 0:012> g (2e40.2d4c): Access violation - code c0000005 (!!! second chance !!!) *** ERROR: Symbol file could not be found. Defaulted to export symbols for F:\Windows\system32\xvidcore.dll - xvid是一种规范的编码格式，它属于mpeg4 part 2规范。那开源的xvid解析器在解析所有的mp4时都是正确的吗？显然不是，看看这一个例子吧。 xvidcore在解析一个畸形的mp4文件时，处理vop可能发生整数下溢。vop可以极为简化的理解为帧，当然两者还是有区别的。一个vop的结构看起来像是： vop_start_code (32 bits - 0x000001B6) vop_coding_type (2 bits - 0=I, 1=P, 2=B, 3=S) modulo_time_base (length varies) marker_bit (1 bit - always 1) vop_time_increment (length varies) marker_bit (1 bit - always 1) vop_coded (1 bit - 0 for NVOPs, 1 otherwise) 0x000001b6 是mpeg4 , part 2 规定的startcode。 在AVS流中，start code是一个特殊的bit形式，每组start code都有start code prefix和start code value。start code prefix是由23个0位和1个1位组成，也即0x00 00 01。所有的start code都是按字节对齐的。 0x000001b6是pb_picture_start_code，这代表了一个P图或者B图的开始。前导帧间预测（P图）和双向帧间预测（B图）由一个2位无符号字符表示，01==P图，02==B图。 一个NAL单元结构如下： 1bit 2bits 5bits payload information |-----|----------|-------------------|----------------- FZB NRI NUT RBSP ^---------------header---------------^ forbidden-zero-bit NAL-ref-ide NAL-unit-type NAL是网络抽象层的缩写，这个东西主要是为了向网络友好的环境传输数据用的。要把AVS视频流映射成NAL单元，只要把每个0x000001的数据映射到NAL单元中，然后在start code之前增加一个1字节的NAL单元头即可。 如果对这个有兴趣的话，可以参考一下这本书： http://books.google.com.hk/books?id=6wfGBAAAQBAJ&pg=PA71&lpg=PA71&dq=0x000001b6&source=bl&ots=ZKA_0DM5aF&sig=WJo619-gGY188gxxDUKaX4z0HJI&hl=zh-CN&sa=X&ei=kUpsVO6wFISlmQXl5oDoDw&ved=0CDMQ6AEwAzgK#v=onepage&q=0x000001b6&f=false
介绍了很多Internet Explorer的函数、类的东西了，让我们实战演练一下，分析一个至2014年10月3日微软尚未修复的Internet Explorer 11中存在的空指针引用问题。 ... 等5天后发布