# IE安全系列之——RES Protocol与打印预览(II)
0x00 简介
=======
* * *
事情得从一个报告说起,有人说网页map.yahoo.co.jp,在IE中进行打印预览时,使用倍率666%会让IE崩溃(至少2016-5-28为止还是这样),为什么IE中会出现这么6的问题呢?从打印预览这个功能看起吧。
![p1](http://drops.javaweb.org/uploads/images/acf4c7fe26c505db87c5a11f559054ad4cef33dd.jpg)
0x01 打印预览(Web)
==============
* * *
从网页的角度来看,负责打印预览的相关内容位于`res://ieframe.dll/preview.dlg`与`res://ieframe.dll/preview.js`。IE自5.5开始引入了打印预览以及自定义的打印预览功能。而微软的早期文档内容十分丰富,可以参考到很多有用信息。
在雅虎网站造成IE的崩溃中,一步不可缺少的操作是——设置缩放比例。所以,我们的关注点可以集中到缩放比率上。
在MSDN介绍自定义打印预览模板时,微软提到:“推荐使用一个DIV作为主容器”。在preview.dlg的LN253-256,可以看到MasterContainer,是一个DIV,这也与微软的推荐做法一致。
“`
“`
另外,还“推荐使用CSS的zoom属性来为主容器设置百分比的缩放”,这在preview.js中得到了良好的体现:
“`
function PositionPages(nDispPage) {
……
MasterContainer.style.zoom = g_nZoomLevel + “%”;
……
“`
与:
“`
function ChangeZoom() {
MasterContainer.style.zoom = g_nZoomLevel + “%”;
PositionPages(g_nDispPage);
return g_nZoomLevel;
}
“`
在了解了缩放功能进行缩放的方法之后,让我们再看一看页面是如何被转入预览中的。
在preview.js的CPrintDoc_AddPage()函数中,可以看到如下的代码注入到了MasterContainer的beforeEnd处。
“`
newHTM = “
newHTM += “
newHTM += “
newHTM += HeadFoot.HtmlHead;
newHTM += “
newHTM += “ “;
newHTM += “
MasterContainer.insertAdjacentHTML(“beforeEnd”, newHTM);
“`
在上面的代码中可以看到一些特殊的元素。比如**IE:DEVICERECT**和**IE:LAYOUTRECT**。DeviceRect、LayoutRect两个元素用来组合展示页面。每个DeviceRect代表一个页面,
LayoutRect为DeviceRect的子元素,IE在它里面显示页面预览。因为preview.dlg指定了XML Namespace(``)所以元素前带有IE:前缀。
与其他元素不同的是,LayoutRect似乎并没有innerHTML属性。IE只是在里面展示页面的预览,页面并不能操作它。那么问题来了,IE怎么产生的预览?
0x02 打印预览窗口的创建
==============
* * *
要知道这个,让我们先完整地跟踪整个流程。首先,因为打印预览是在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])
…………………………
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,那么这个调用是谁发起的呢?观察其他线程,可以发现下列调用栈(无关栈已经删除):
“`
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
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
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)); 。
“`
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,也即由``元素的**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](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);
“`
0x04 CreateHTMLDocSource
========================
* * *
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
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
return v8;
}
“`
函数创建HTMLDocumentSource类,并调用_Initialize函数进行初始化。构造函数的代码简单易懂,全部都是赋初值的,跳过不叙述了。接下来查看_Initialize。
_Initialize是一个较长的函数,但是逻辑较为清晰,我把所有IDA没有识别出的guid等全部以注释的形式标记了。该函数先获取了IHTMLEventObj2接口,然后识别环境并设置浏览模式为`__IE_Immersive`(Win8 Immersive Mode),若Immersive Mode生效,再设置成`__IE_ShrinkToFit`(Shrink to fit模式,该模式下浏览器自动将页面收缩成方便打印的大小)模式。
![p2](http://drops.javaweb.org/uploads/images/c64876a458a2ee7f5d51dac80423e82f343d8090.jpg)
然后,调用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
v6 = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, 0, 1u, &_GUID_00000146_0000_0000_c000_000000000046, v5); //guid of IGlobalInterfaceTable
if ( v6 >= 0 )
{
v7 = TSmartPointer
v6 = HTMLDocumentSource::QueryInterface(v4, &_GUID_00000000_0000_0000_c000_000000000046, v7); //querying IID_IUnknown
if ( v6 >= 0 )
{
v8 = punkOuter;
v9 = (LPUNKNOWN *)TSmartPointer
v6 = CoCreateFreeThreadedMarshaler(v8, v9);
if ( v6 >= 0 )
{
v51 = 0;
v10 = TSmartPointer
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
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
}
}
}
TSmartPointer
return v6;
}
“`
最后,通过InternalModelessDialog启动该窗口实现预览
“`
HRESULT __usercall InternalModelessDialog
“`
那么这个预览窗口中显示的文档和其他的到底有什么不同呢?根据MSDN文档的意思是:有点像禁止了Script的WebBrowser。代码将Layout当作一个容器来显示预览后的内容。在Modeless Window中,我们不能F12,不能呼出右键菜单,怎么办?我们有另一种方法——通过IE暴露的接口来取得整个窗口的DOM内容。
0x05 使用C++获取打印预览窗口的body.outerHTML
=================================
* * *
这个逻辑中,预览的窗口标题为“打印预览”。可以使用FindWindow找到对应的窗口并枚举出正确的Server窗口,或是使用Spy++找到窗口的HWND(因为这里我们并不是写一个通用的工具,所以怎么方便怎么来)。
注意目标窗口是Internet Explorer_Server而不是外层的Internet Explorer_TridentDlgFrame。这里使用的代码如下:
“`
// prjFindPrintview.cpp : 定义控制台应用程序的入口点。
//
#include “stdafx.h”
#include
#include
#include
#include
#include
#include
#include
#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
hr = ObjectFromLresult(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc);
if (FAILED(hr))
return -1;
CComPtr
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
if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement)))
continue;
CComPtr
if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement)))
continue;
CComBSTR outerHTML;
spElement->get_outerHTML(&outerHTML);
OutputDebugStringW(outerHTML);
}
::CoUninitialize();
return 0;
}
“`
取得窗口的DOM如下:
![p3](http://drops.javaweb.org/uploads/images/f266a5f63821a067d79f2771f073382afe413d2a.jpg)
将其输出到文件,自行添上`