Blast, 2016/05/30 nakedness

0 x00 profile


It started with a report that map.Yahoo.co.jp used 666% of its power to print preview in IE (at least until May 28, 2016). Why did IE have such a 6% problem? Start with print preview.

0x01 Print Preview (Web)


From the point of view of web pages, is responsible for the print preview of the related contents in res: / / ieframe. DLL/preview DLG and res: / / ieframe. DLL/preview. Js. Internet Explorer 5.5 introduced print preview and custom print preview. Microsoft’s early documentation is rich with useful information.

In the Yahoo crash of Internet Explorer, one of the indispensable steps is to set the zoom scale. So, we can focus on scaling.

In MSDN’s introduction to custom print preview templates, Microsoft says that “it is recommended to use a DIV as the main container.” In the preview. DLG ln253-256, you can see that MasterContainer is a DIV, which is also in line with Microsoft’s recommended practice.

#! html <div id="MasterContainer" tabindex="0" style="width:100%; position:absolute;" > <! -- Pages go here --> <div id="EmptyPage" class="divPage" style="position:absolute; left:0px; top:0px; display:none;" ><div class="page">&nbsp; </div></div> </div>Copy the code

In addition, it is “recommended to use the ZOOM property of CSS to set the percentage zoom for the main container”, which is well reflected in preview.js:

#! Js function PositionPages(nDispPage) {... MasterContainer.style.zoom = g_nZoomLevel + "%"; ...Copy the code

And:

#! js function ChangeZoom() { MasterContainer.style.zoom = g_nZoomLevel + "%"; PositionPages(g_nDispPage); return g_nZoomLevel; }Copy the code

Now that you know how the zoom function zooms in, let’s take a look at how the page is rolled into preview.

In the preview.js CPrintDoc_AddPage() function, you can see the following code injected into the beforeEnd of MasterContainer.

#! js newHTM = "<DIV class=divPage><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 += "<DIV class='" + classHeader + "' id=header>"; newHTM += HeadFoot.HtmlHead; newHTM += "</DIV>"; newHTM += "<DIV class='" + classFooter + "' id=footer>"; newHTM += HeadFoot.HtmlFoot; newHTM += "</DIV>"; newHTM += "</IE:DeviceRect></DIV>"; MasterContainer.insertAdjacentHTML("beforeEnd", newHTM);Copy the code

You can see some special elements in the code above. IE:DEVICERECT and IE:LAYOUTRECT. The elements DeviceRect and LayoutRect are used to combine the display page. Each DeviceRect represents a page,

LayoutRect is a child element of DeviceRect in which IE displays a preview of the page. Because preview.dlg specifies XML Namespace (), elements are prefixed with IE:.

Unlike other elements, LayoutRect does not appear to have an innerHTML attribute. IE just shows a preview of the page, the page does not manipulate it. So the question is, how does IE generate previews?

0x02 Print preview window created


To know this, let’s trace the entire process first. First, since the print preview is under RES: Protocol, we saw in the previous article that CResProtocol is the res Protocol class, and the full call can be seen at the DoParseAndBind breakpoint (irrelevant stacks have been removed) :

#! bash 0:058> bp MSHTML! CResProtocol: : DoParseAndBind 0: brand Breakpoint 0 hit eax = 14061280 > 058 g ebx ecx = 08101 = 140611 fc bd4 edx = 1406127 c esi c2f1fc = 76  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] 4163) 01 120 64750495 120 64750410 140611 e0 ae2b0 ae268 MSHTML! CResProtocol: : ParseAndBind + 0 x26 (FPO: 0, 0) 02 06 e8c9f0 ae290 c63067 fc 140611 76 120 08109 f38 MSHTML! CResProtocol: : Start + 0 x88 (FPO:,5,4 [6])................................................ 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 + 0 x12f (FPO: [Non - FPO])..........................................Copy the code

The Window is a Modeless Window based on the 15 stacks, so who initiated the call? Looking at other threads, you can see the following call stacks (extraneous stacks have been removed) :

#! Bash 43 Id: 5404.3b18 Suspend: 1 Teb: 7ef7e000 Unfrozen # ChildEBP ………………………… 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 + 0 x98d (FPO: [5115 7, 4]) 0 e ba3c f4aaac bc898 004 64 1090 00000000 1090 ba70 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 + 0 x2b4 (FPO:,85,4 [7])................................................Copy the code

You can find

  1. MSHTML! CDoc::PrintHandler handles the associated print requests;
  2. Final page has launched a new Internet explorer window to load res: / / ieframe. DLL/preview DLG (ieframe! CDocHostUIHandler: : DoTemplatePrinting – MSHTML! ShowHTMLDialogEx).

0x03 CDoc::PrintHandler


With that in mind, let’s look at MSHTML! CDoc: : PrintHandler. Open mshtml. DLL in IDA, locate CDoc::PrintHandler, and view the pseudo-code.

First CDoc::PrintHandler checks if the specified CLSID is supported. PCommandTarget ->Exec(&cgid_dochostCommandHandler, a10 (i.e. Guid), a7, &varin (i.e. V73), &varout (i.e. V67)); .

#! cpp 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; }Copy the code

Next, the program looks for alternative print sources, that is, alternative print sources specified by REL=alternate MEDIA=print of the element. If not, do some simple string manipulation:

#! cpp 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; }}Copy the code

And then there’s a long function section. First, the program determines whether there is a printable plugin site. Acrobat PDF, for example, is one. Then send the “Print” request. MSDN is “print with default template or custom template” (msdn.microsoft.com/en-us/libra…) .

#! cpp v73 = 0; if( ! CDoc::GetPlugInSiteForPrinting(v72, &v73) ) { v18 = v73; if ( a10 ) { v11 = -2147467259; } else { memset(&v50, 0, 0x20u); v69 = 0; Cookie = 0; 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; }Copy the code

Then, you can see that the PrintHandler saves the current page to a temporary directory.

#! cpp 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; }...Copy the code

Then SetPrintCommandParameters, SetPrintManagerCommandParameters used to set the print parameters and skip first, then a key CreateHTMLDocSource operation, is responsible for the hero of the pop-up window printing.

#! cpp v11 = CreateHTMLDocSource(v61, v39, v40, v38, (struct IInspectable **)v48, v49);Copy the code

0x04 CreateHTMLDocSource


CreateHTMLDocSource CreateHTMLDocSource

#! cpp HRESULT __fastcall CreateHTMLDocSource(int a1, int a2, int a3, struct IWebPlatformHostSecurityManagerFactory *a4, struct IInspectable **a5, bool a6) { HRESULT v6; //[email protected]
  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;
}
Copy the code

Call HTMLDocumentSource: : Create and QueryInterface and returns in the parameters of the a3. See HTMLDocumentSource: : Create code, is also a short function:

#! cpp HRESULT __fastcall HTMLDocumentSource::Create(int a1, int a2, int a3, char a4) { int v4; //[email protected]
  int v5; // [email protected]
  LPVOID v6; // [email protected]
  int v7; // [email protected]
  HRESULT v8; // [email protected]
  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;
}
Copy the code

The HTMLDocumentSource () function creates the HTMLDocumentSource class and calls _Initialize to initialize it. The constructor code is straightforward and all initial assignments are skipped. Next look at _Initialize.

_Initialize is a long function, but the logic is clear. I annotate all guids that IDA doesn’t recognize. The Immersive Mode goes to the IHTMLEventObj2 interface, identifies the environment and sets it to __IE_Immersive (Win8 Immersive Mode). Set to __IE_ShrinkToFit (Shrink to fit mode, in which the browser automatically shrinks pages to a size that makes them easier to print).

Then, call PrintManagerOptions: : Create. This will create a PrintManagerOptions object and call its _Initialize method. All it does is get its IUnkonwn interface, so we’ll skip that too.

Next, try to open the IE “res: / / ieframe. DLL/preview. DLG”, familiar words. Tells the handler about Settings such as temporary file locations.

#! cpp HRESULT __thiscall HTMLDocumentSource::_Initialize(void *this, int a2, int a3, char a4) { int v4; //[email protected]
  LPVOID *v5; // [email protected]
  HRESULT v6; // [email protected]
  int v7; // [email protected]
  IUnknown *v8; // [email protected]
  LPUNKNOWN *v9; // [email protected]
  int v10; // [email protected]
  int v11; // [email protected]
  int v12; // [email protected]
  const WCHAR *v13; // [email protected]
  BSTR v14; // [email protected]
  LPMONIKER v15; // [email protected]
  struct IMonikerVtbl *v16; // [email protected]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; }Copy the code

Finally, the window implementation preview is launched via InternalModelessDialog

#! cpp HRESULT __usercall InternalModelessDialog<eax>(int a1<edx>, int a2<ecx>, HRESULT a3<esi>)Copy the code

So what’s the difference between the documents in this preview window and the others? According to the MSDN documentation: sort of like WebBrowser with Script disabled. The code uses the Layout as a container to display the previewed content. In Modeless Window, we can’t F12, can’t exhale right click menu, what should we do? There is another way to retrieve the DOM content of the entire window through the interface exposed by IE.

0x05 use C++ to get the body.outerhtml of the print preview window


In this logic, the preview window is titled print Preview. You can use FindWindow to find the corresponding window and enumerate the correct Server window, or Spy++ to find HWND for the window (since we are not writing a generic tool here, we can do whatever is convenient).

Note that the target window is Internet Explorer_Server and not the outer Internet Explorer_TridentDlgFrame. Here is the code used:

#! CPP // prjFindPrintView.cpp: Defines the entry point for the console application. // #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; }Copy the code

Get the window DOM as follows:

Output it to a file and add your own to complete it. Keep in mind that Layout and other elements are only valid in the print preview, so don’t be confused if you can’t extract them separately. This is just for the sake of understanding the final DOM distribution in the preview.

Next, let’s relive the crash. Open map.Yahoo.co.jp, access the code that crashed, and right-click to preview. Only this time we use cache files to simplify the code. After the preview, copy all the newly generated HTM files from under %TEMP%, and then adjust the print preview to 666%. Once you’re sure you can still crash, let’s start removing the useless data and note down the abnormal offset E9b102 (that’s the number on my machine, because some crashes can turn into other crashes when you’re trimming code, so it’s a good idea to note down the offset address in case a new problem arises from somewhere along the way). After a lot of streamlining, we were left with the following code:

#! html <! DOCTYPE HTML> <HTML><HEAD> </HEAD> <BODY> <DIV style="left: -5000px; top: -5000px; width: 10000px; height: 10000px; text-align: justify; position: absolute; z-index: 0;" ><svg xmlns="http://www.w3.org/2000/svg" style="position: relative;" viewBox="0 0 10000 10000" width="10000" height="10000" /> </DIV> </BODY></HTML>Copy the code

And the simplification to this step can be found, as long as the launch of print preview, IE will crash directly. So, to make IE crash automatically, we call print() by hand; Can.

#! html <! DOCTYPE HTML> <HTML><HEAD> </HEAD> <BODY> <DIV style="left: -5000px; top: -5000px; width: 10000px; height: 10000px; text-align: justify; position: absolute; z-index: 0;" ><svg xmlns="http://www.w3.org/2000/svg" style="position: relative;" viewBox="0 0 10000 10000" width="10000" height="10000" /> </DIV> <script>print(); </script> </BODY></HTML>Copy the code

Following debugging, it can be found that the crash location remains unchanged:

#!bash
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=????????
Copy the code

Let’s look at the DOM structure of the print preview window. The DIV STYLE attribute is one of the key conditions for triggering a crash, but we want to get the DOM in the preview window, so we need to make some changes. First we manually remove some of the STYLE, and then we preview.

I’ll skip the DOM and start with the simplified code above.

0x06 Crash Analysis


View the crash stack after the crash as follows:

#! bash 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 + 0 x89 (FPO:,51,4 [1]) 03 64 d82d0e0 b2dfb3c b410d7 0 0 0 0 b2df87c b2df8e4 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 + 0 x1a0 (FPO:,9,4 [1]) 0 0 c b2dfbcc a7d7f0 MSHTML 00000000 64592161 00100000 64! 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 + 0 x1a0 (FPO:,13,4 [1])Copy the code

MSHTML! CDispParentNode: : ComputeVisibleBoundsOnDirtySVGSubtree is a Thiscall, code is very simple, is called a member function:

#! cpp 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); }Copy the code

Looking at the function code, it is clear that the this pointer is null.

#! bash 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 = *ebxCopy the code

Check the previous layer

#! bash 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)Copy the code

Obviously MSHTML! Layout: : LayoutBox: : GetDisplayNodeAsParent returns NULL.

#! cpp int __thiscall Layout::LayoutBox::GetDisplayNodeAsParent(void *this) { int pThisVar; //[email protected]
  int nResult; // [email protected]
  int v3; // [email protected]
  int v4; // [email protected]
  int v5; // [email protected]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; }Copy the code

You can simply trace the code to see what went wrong. To avoid repeated breakpoints (Layout calls are frequent), you can

bp MSHTML! ModelessThreadProcCopy the code

After the trigger

bc 0 bp MSHTML! Layout::FlowBoxBuilder::BuildBoxItemCopy the code

After the trigger

bc 0 bp MSHTML! Layout::LayoutBox::GetDisplayNodeAsParentCopy the code

That’s fine

#! bash 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+0xceCopy the code

I can trace it again

#! cpp pThisVar = (*(int (**)(void))(*(_DWORD *)this + 788))(); // MSHTML! Layout::SvgBox::GetDisplayNode //returns null nResult = pThisVar; if ( ! pThisVar ) goto Cleanup; //return NULL(0);Copy the code

Looking at GetDisplayNode, it’s clear that the checksum failed and returns 0.

#! cpp int __thiscall Layout::SvgBox::GetDisplayNode(int this) { int result; //[email protected]if ( ! (*(_BYTE *)(this + 56) & 4) || *(_BYTE *)(*(_DWORD *)(__readfsdword(44) + 4 * LODWORD(_tls_index)) + 36) ) result = *(_DWORD *)(this + 16); else result = 0; return result; }Copy the code

With memory tracing, we can conclude that this is a null-pointer problem. However, it also gives a warning – can some problems that cannot be triggered in the web page be done through print preview? Fuzzer seems to be able to add this strategy and test the print preview to see if it is robust.