<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/tips/16275

                    0x00 簡介


                    事情得從一個報告說起,有人說網頁map.yahoo.co.jp,在IE中進行打印預覽時,使用倍率666%會讓IE崩潰(至少2016-5-28為止還是這樣),為什么IE中會出現這么6的問題呢?從打印預覽這個功能看起吧。

                    p1

                    0x01 打印預覽(Web)


                    從網頁的角度來看,負責打印預覽的相關內容位于res://ieframe.dll/preview.dlgres://ieframe.dll/preview.js。IE自5.5開始引入了打印預覽以及自定義的打印預覽功能。而微軟的早期文檔內容十分豐富,可以參考到很多有用信息。

                    在雅虎網站造成IE的崩潰中,一步不可缺少的操作是——設置縮放比例。所以,我們的關注點可以集中到縮放比率上。

                    在MSDN介紹自定義打印預覽模板時,微軟提到:“推薦使用一個DIV作為主容器”。在preview.dlg的LN253-256,可以看到MasterContainer,是一個DIV,這也與微軟的推薦做法一致。

                    #!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>
                    

                    另外,還“推薦使用CSS的zoom屬性來為主容器設置百分比的縮放”,這在preview.js中得到了良好的體現:

                    #!js
                    function PositionPages(nDispPage) {
                    ……
                            MasterContainer.style.zoom = g_nZoomLevel + "%";
                    ……
                    

                    與:

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

                    在了解了縮放功能進行縮放的方法之后,讓我們再看一看頁面是如何被轉入預覽中的。

                    在preview.js的CPrintDoc_AddPage()函數中,可以看到如下的代碼注入到了MasterContainer的beforeEnd處。

                    #!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);
                    

                    在上面的代碼中可以看到一些特殊的元素。比如IE:DEVICERECTIE:LAYOUTRECT。DeviceRect、LayoutRect兩個元素用來組合展示頁面。每個DeviceRect代表一個頁面,

                    LayoutRect為DeviceRect的子元素,IE在它里面顯示頁面預覽。因為preview.dlg指定了XML Namespace(<HTML XMLNS:IE>)所以元素前帶有IE:前綴。

                    與其他元素不同的是,LayoutRect似乎并沒有innerHTML屬性。IE只是在里面展示頁面的預覽,頁面并不能操作它。那么問題來了,IE怎么產生的預覽?

                    0x02 打印預覽窗口的創建


                    要知道這個,讓我們先完整地跟蹤整個流程。首先,因為打印預覽是在res: protocol下的,之前的文章我們介紹了CResProtocol是處理Res Protocol的類,在DoParseAndBind上下斷點可以觀察到此處完整的調用(無關的棧已經刪除):

                    #!bash
                    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])
                    …………………………
                    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])
                    ……………………
                    

                    根據15層棧可以知道該窗口是一個Modeless Window,那么這個調用是誰發起的呢?觀察其他線程,可以發現下列調用棧(無關棧已經刪除):

                    #!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+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])
                    …………………………
                    

                    可以發現

                    1. MSHTML!CDoc::PrintHandler處理了相關的打印請求;
                    2. 最終頁面啟動了一個新IE窗口用來加載res://ieframe.dll/preview.dlg(IEFRAME!CDocHostUIHandler::DoTemplatePrinting → MSHTML!ShowHTMLDialogEx)。

                    0x03 CDoc::PrintHandler


                    有了之前的結論之后,首先來觀察MSHTML!CDoc::PrintHandler。在IDA中打開MSHTML.DLL,定位到CDoc::PrintHandler后查看偽代碼(下為Win7+IE11的結果)。

                    首先CDoc::PrintHandler檢查指定的CLSID是否被支持。下偽代碼實際含義pCommandTarget->Exec(&CGID_DocHostCommandHandler, a10(即:guid), a7, &varIn (即:v73), &varOut (即: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;
                    }
                    

                    接著,程序尋找是否有alternative print source,也即由<link>元素的REL=alternate MEDIA=print指定的其他可選打印源。如果沒有,做一些簡單的字符串處理工作:

                    #!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;
                        }
                      }
                    

                    再接下來是一個比較長的函數段。首先程序判斷是否有可打印的plugin site。例如acrobat pdf都算是這種。然后發送“Print”請求。MSDN解釋是“用默認模版或自定義模版”來打印(https://msdn.microsoft.com/en-us/library/aa769937(v=vs.85).aspx)。

                    #!cpp
                    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將當前頁面保存到了臨時目錄下。

                    #!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;
                      }
                      ......
                    

                    隨后,SetPrintCommandParameters、SetPrintManagerCommandParameters用來設置打印參數,先跳過不看,之后有一個關鍵操作CreateHTMLDocSource,則是負責彈出打印窗口的主人公。

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

                    0x04 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;
                    }
                    

                    調用HTMLDocumentSource::Create并QueryInterface并返回在參數a3中。查看HTMLDocumentSource::Create的代碼,也是一個短小的函數:

                    #!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;
                    }
                    

                    函數創建HTMLDocumentSource類,并調用_Initialize函數進行初始化。構造函數的代碼簡單易懂,全部都是賦初值的,跳過不敘述了。接下來查看_Initialize。

                    _Initialize是一個較長的函數,但是邏輯較為清晰,我把所有IDA沒有識別出的guid等全部以注釋的形式標記了。該函數先獲取了IHTMLEventObj2接口,然后識別環境并設置瀏覽模式為__IE_Immersive(Win8 Immersive Mode),若Immersive Mode生效,再設置成__IE_ShrinkToFit(Shrink to fit模式,該模式下瀏覽器自動將頁面收縮成方便打印的大小)模式。

                    p2

                    然后,調用PrintManagerOptions::Create。這個操作將創建一個PrintManagerOptions對象,并調用其_Initialize方法,干的事情也就是拿到它的IUnkonwn接口,所以這塊我們也跳過。

                    接下來,IE試圖打開“res://ieframe.dll/preview.dlg”,很熟悉的字眼。告知該處理程序臨時文件位置等設置。

                    #!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;
                    }
                    

                    最后,通過InternalModelessDialog啟動該窗口實現預覽

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

                    那么這個預覽窗口中顯示的文檔和其他的到底有什么不同呢?根據MSDN文檔的意思是:有點像禁止了Script的WebBrowser。代碼將Layout當作一個容器來顯示預覽后的內容。在Modeless Window中,我們不能F12,不能呼出右鍵菜單,怎么辦?我們有另一種方法——通過IE暴露的接口來取得整個窗口的DOM內容。

                    0x05 使用C++獲取打印預覽窗口的body.outerHTML


                    這個邏輯中,預覽的窗口標題為“打印預覽”。可以使用FindWindow找到對應的窗口并枚舉出正確的Server窗口,或是使用Spy++找到窗口的HWND(因為這里我們并不是寫一個通用的工具,所以怎么方便怎么來)。

                    注意目標窗口是Internet Explorer_Server而不是外層的Internet Explorer_TridentDlgFrame。這里使用的代碼如下:

                    #!cpp
                    // 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如下:

                    p3

                    將其輸出到文件,自行添上</BODY></HTML>即為完整的內容。不過需要記得的是Layout等元素只在打印預覽中有效,所以單獨提取出來崩潰不了也不要太疑惑,這里只是為了方便了解預覽中最終的DOM分布情況而已。

                    接下來,讓我們復現崩潰吧。打開map.yahoo.co.jp,訪問出現崩潰的代碼,右鍵預覽。只不過這次我們使用緩存文件的方式來精簡代碼。預覽后,從%TEMP%下復制走所有新生成的HTM文件,然后調節打印預覽的比例為666%。在確定仍然可以崩潰之后,讓我們開始刪除無用數據,記下異常偏移e9b102(在我的機器上是這個數字,因為有些崩潰在精簡代碼時可能變成其他的崩潰,所以最好記下偏移地址,以免中途不知道從什么位置產生了新問題)。多次精簡之后,我們留下了下列代碼:

                    #!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>
                    

                    而且精簡到這一步就可以發現,只要發起打印預覽,IE就會直接崩潰。所以,為了讓IE自動觸發崩潰,我們手寫一下調用print();即可。

                    #!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>
                    

                    跟隨調試可以發現,崩潰位置維持不變:

                    #!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=????????
                    

                    接下來讓我們看看打印預覽窗口的DOM結構。因為DIV的STYLE屬性是觸發崩潰的關鍵條件之一,但是我們又想拿到預覽窗口的DOM,所以我們得做一些改動,先手動刪除部分STYLE,然后預覽,這時,便可使用我們之前的程序抓出DOM。

                    篇幅考慮我就不扯淡再去看這個DOM了,直接以上述精簡后的代碼開始分析工作。

                    0x06 崩潰分析


                    在崩潰后查看崩潰棧如下:

                    #!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+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,代碼十分簡單,就是調用自己一個成員函數:

                    #!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);
                    }
                    

                    查看函數代碼,很明顯,this指針為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 = *ebx
                    

                    查看上一層

                    #!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)
                    

                    很明顯MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent返回了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;
                    }
                    

                    可以簡單跟蹤一下代碼看看到底是哪兒出現了問題。為了避免重復斷到斷點上(Layout的調用是十分頻繁的),可以

                    bp MSHTML!ModelessThreadProc 
                    

                    觸發后

                    bc 0
                    bp MSHTML!Layout::FlowBoxBuilder::BuildBoxItem
                    

                    觸發后

                    bc 0
                    bp MSHTML!Layout::LayoutBox::GetDisplayNodeAsParent
                    

                    這樣就沒問題了

                    #!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+0xce
                    

                    再度跟蹤可以發現是

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

                    查看GetDisplayNode,很明顯,校驗失敗,返回了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;
                    }
                    

                    加上對內存的跟蹤,我們可以斷定這是一個空指針問題。不過這倒也給了一個提醒——是否一些在網頁內不能觸發的問題,通過打印預覽就又可以做了呢?Fuzzer看來也可以加上這個策略,試一試打印預覽是否健壯。

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频