IE的FREAK对策(a)

对IE来说,FREAK的就是在握手交换密钥的时候,服务器把高强度的密钥丢弃,转而使用512位RSA密钥和浏览器(客户端)协商的过程。针对这个问题,Firefox和Chrome都能正常处理,可惜IE、Safari和Opera(Linux)都是一贯的死相,该出问题的还是出问题。另外,Mozilla的这个问题很久前就修复了。要说为啥有这个问题,还是怪老美年轻的时候想着监控全球搞出来的一堆恶心玩意儿。

IE自己用的是Schannel库,从Wireshark抓包上看,明显出现了(服务器)server hello - server key exchange 的过程,在firefox下这个过程被直接拒掉了server hello - alert - connection fail,IE倒是来者不拒完成了协商,同时将密钥降级成RSA(512位交换)。

以下是在CVE Test上IE最终的结果:TLS 1.1, AES (128 位加密 (高)); RSA(512 位交换)

现在512位的估计一台机器跑几天就出来了,所以这个东西明显是不安全的。但是怎么修啊?微软给出来了Security Advisor,但是明显不靠谱,他也不标明是客户端用的还是服务器端用的,浪费了我两个小时的测试,明显它这招不能用在客户端上,而且受限于微软的gpedit.msc输入框最多只能输入1023字节,它每个算法的名字还特别长,所以微软干脆就把……把他的支持加密方式给删了一部分。

然后现在首要的就是获得这么一个信息: 我怎么能方便快捷的知道现在IE连接是什么情况?明显ie的属性-连接可以告诉我们这一切,但是这个怎么获取呢?开始我天真的以为可以用bp user32!SetWindowTextA/W的方式来获取,可是出人意料的是,属性窗口里面居然也是一个Internet Explorer_Server。(这一点你从IE11的属性就可以发现,因为它的“证书”按钮明显和“确定”按钮的样式不一样)

好吧,现在我们有一个很脏的方式:先弹出属性,然后找到窗口句柄,获取到里面Internet Explorer_Server的句柄,然后获取它的IHTMLDocument2对象,然后获取它的innerHTML。 看起来就很复杂,先实现一下。

这时,还是得用上bp user32!SetWindowTextA/W的方法,首先获取到设置连接的位置:

Breakpoint 1 hit
eax=00000002 ebx=02683ae0 ecx=00000000 edx=00000007 esi=0012b9b0 edi=000500be
eip=77d2960e esp=0012b894 ebp=0012bb7c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00040246
USER32!SetWindowTextW:
77d2960e 8bff            mov     edi,edi
0:000> du poi(esp+0x8)
0012b9b0  "属性"
0:000> kvn
 # ChildEBP RetAddr  Args to Child              
00 0012b890 7718ac1b 000500be 0012b9b0 00000000 USER32!SetWindowTextW (FPO: [Non-Fpo])
01 0012bb7c 7718afcd 000500be 02683ae0 000600bc comctl32!_SetTitle+0xf9 (FPO: [Non-Fpo])
02 0012bf3c 7718c695 000500be 02683ae0 02683ae0 comctl32!InitPropSheetDlg+0x80 (FPO: [Non-Fpo])
03 0012bfac 77d18734 000500be 00000110 000600bc comctl32!PropSheetDlgProc+0x4cb (FPO: [Non-Fpo])
04 0012bfd8 77d2413c 7718c1ca 000500be 00000110 USER32!InternalCallWinProc+0x28
05 0012c044 77d23b30 0014bab8 7718c1ca 000500be USER32!UserCallDlgProcCheckWow+0xf0 (FPO: [Non-Fpo])
06 0012c08c 77d23d5c 00000000 00000110 000600bc USER32!DefDlgProcWorker+0xa8 (FPO: [Non-Fpo])
07 0012c0a8 77d18734 000500be 00000110 000600bc USER32!DefDlgProcW+0x22 (FPO: [Non-Fpo])
08 0012c0d4 77d18816 77d23d3a 000500be 00000110 USER32!InternalCallWinProc+0x28
09 0012c13c 77d2927b 0014bab8 77d23d3a 000500be USER32!UserCallWinProcCheckWow+0x150 (FPO: [Non-Fpo])
0a 0012c178 77d2651a 00619fd8 00603980 000600bc USER32!SendMessageWorker+0x4a5 (FPO: [Non-Fpo])
0b 0012c230 77d2683e 00000000 00619fd8 000000e0 USER32!InternalCreateDialog+0x9df (FPO: [Non-Fpo])
0c 0012c254 77d3f03a 77180000 001f4760 00030126 USER32!CreateDialogIndirectParamAorW+0x33 (FPO: [Non-Fpo])
0d 0012c274 7720b9a1 77180000 001f4760 00030126 USER32!CreateDialogIndirectParamW+0x1b (FPO: [Non-Fpo])
0e 0012c2bc 7718ccc7 77180000 001f4760 00030126 comctl32!SHFusionCreateDialogIndirectParam+0x36 (FPO: [Non-Fpo])
0f 0012c320 7718cf39 00030126 77d290b4 0012c398 comctl32!_RealPropertySheet+0x242 (FPO: [Non-Fpo])
10 0012c338 7718cf54 0012c398 00000000 0012c38c comctl32!_PropertySheet+0x146 (FPO: [Non-Fpo])
11 0012c348 77157047 0012c398 77d290b4 00000000 comctl32!PropertySheetW+0xf (FPO: [Non-Fpo])
12 0012c38c 77157d50 00000000 00000034 00000501 OLEAUT32!COlePropertySheet::DoModal+0x2a (FPO: [Non-Fpo])
13 0012c3ec 7e5a6806 0012c414 7e25689c 7e558bc0 OLEAUT32!OleCreatePropertyFrameIndirect+0x28 (FPO: [Non-Fpo])
14 0012c494 7e5a6adc 002dc857 00000000 00000000 SHDOCVW!CDocHostUIHandler::ShowPropertysheetDialog+0x1f9 (FPO: [Non-Fpo])
15 0012c4a8 7e58bf11 00151808 7e25688c 0000000a SHDOCVW!CDocHostUIHandler::Exec+0xab (FPO: [Non-Fpo])
16 0012d74c 7e57ff44 0017782c 7e25688c 0000000a SHDOCVW!CDocObjectHost::OnExec+0xae2 (FPO: [Non-Fpo])
17 0012d77c 7e355e07 00000000 7e25688c 0000000a SHDOCVW!CDocObjectHost::Exec+0x101 (FPO: [Non-Fpo])
18 0012d8b4 7e315ec5 01f407f0 0004010a 00000000 mshtml!CDoc::ShowPropertyDialog+0x10b (FPO: [Non-Fpo])
19 0012dc4c 7e29bba5 00000000 7e55890c 0000001c mshtml!CDoc::ExecHelper+0x252 (FPO: [Non-Fpo])
1a 0012dc6c 7e5a4960 001c4af8 7e55890c 0000001c mshtml!CDoc::Exec+0x1e (FPO: [Non-Fpo])
1b 0012dd58 7e5aa6b7 00151800 0000001c 0012ddb4 SHDOCVW!CDocHostUIHandler::ShowContextMenu+0x436 (FPO: [Non-Fpo])
1c 0012dd84 7e395765 00177810 00000000 0012ddb4 SHDOCVW!CDocObjectHost::ShowContextMenu+0x88 (FPO: [Non-Fpo])
1d 0012ddc8 7e36cf18 0000017b 0000013a 00000000 mshtml!CDoc::ShowContextMenu+0xee (FPO: [Non-Fpo])
1e 0012dde0 7e36cee2 0000017b 0000013a 00000000 mshtml!CElement::ShowContextMenu+0x1b (FPO: [Non-Fpo])
1f 0012deec 7e330067 0000013a 0000013a 00000000 mshtml!CElement::OnContextMenu+0xb0 (FPO: [Non-Fpo])
20 0012dfa4 7e2f6e72 02061f00 0012e1c8 0205ddc0 mshtml!CLayout::HandleMessage+0x2b9 (FPO: [Non-Fpo])
21 0012e088 7e2f69c9 02061f00 0012e1c8 0205de40 mshtml!CFlowLayout::HandleMessage+0x64f (FPO: [Non-Fpo])
22 0012e0a4 7e2f6647 0205ddc0 0012e1c8 0205de40 mshtml!CElement::HandleMessage+0x90 (FPO: [Non-Fpo])
23 0012e120 7e2e8cfa 0205de40 0205de40 00000000 mshtml!CDoc::PumpMessage+0x93c (FPO: [Non-Fpo])
24 0012e26c 7e2e7c78 0000007b 00000000 013a017b mshtml!CDoc::OnMouseMessage+0x3b6 (FPO: [Non-Fpo])
25 0012e38c 7e278a1a 00000000 0000007b 0004010a mshtml!CDoc::OnWindowMessage+0xc43 (FPO: [Non-Fpo])
26 0012e4c4 77d18734 0004010a 0000007b 0004010a mshtml!CServer::WndProc+0x9f (FPO: [Non-Fpo])
27 0012e4f0 77d18816 7e2789a4 0004010a 0000007b USER32!InternalCallWinProc+0x28
28 0012e558 77d2bf15 00000000 7e2789a4 0004010a USER32!UserCallWinProcCheckWow+0x150 (FPO: [Non-Fpo])
29 0012e5ac 77d28dd9 0004010a 00000205 00000000 USER32!RealDefWindowProcWorker+0x107a (FPO: [Non-Fpo])
2a 0012e5c8 77d28d77 0004010a 00000205 00000000 USER32!RealDefWindowProcW+0x47 (FPO: [Non-Fpo])
2b 0012e610 7e2f7668 0004010a 00000205 00000000 USER32!DefWindowProcW+0x72 (FPO: [Non-Fpo])
2c 0012e634 7e2f66b1 00000205 00000000 00bf0145 mshtml!CServer::OnDefWindowMessage+0x68 (FPO: [Non-Fpo])
2d 0012e6b8 7e2e8cfa 0205de40 00000000 00000000 mshtml!CDoc::PumpMessage+0xa5c (FPO: [Non-Fpo])
2e 0012e804 7e2e7c78 00000205 00000000 00bf0145 mshtml!CDoc::OnMouseMessage+0x3b6 (FPO: [Non-Fpo])
2f 0012e924 7e278a1a 00000000 00000205 00000000 mshtml!CDoc::OnWindowMessage+0xc43 (FPO: [Non-Fpo])
30 0012ea5c 77d18734 0004010a 00000205 00000000 mshtml!CServer::WndProc+0x9f (FPO: [Non-Fpo])
31 0012ea88 77d18816 7e2789a4 0004010a 00000205 USER32!InternalCallWinProc+0x28
32 0012eaf0 77d189cd 00000000 7e2789a4 0004010a USER32!UserCallWinProcCheckWow+0x150 (FPO: [Non-Fpo])
33 0012eb50 77d18a10 0012eb90 00000000 0012eb78 USER32!DispatchMessageWorker+0x306 (FPO: [Non-Fpo])
34 0012eb60 75f0d875 0012eb90 00000000 00152178 USER32!DispatchMessageW+0xf (FPO: [Non-Fpo])
35 0012eb78 75f15218 0012eb90 0012ee98 00000000 BROWSEUI!TimedDispatchMessage+0x33 (FPO: [Non-Fpo])
36 0012edd8 75f15389 00151f10 0012ee98 00151f10 BROWSEUI!BrowserThreadProc+0x336 (FPO: [Non-Fpo])
37 0012ee6c 75f15655 00151f10 00151f10 00000000 BROWSEUI!BrowserProtectedThreadProc+0x50 (FPO: [Non-Fpo])
38 0012fef0 7e5d8d7a 00151f10 00000000 00000000 BROWSEUI!SHOpenFolderWindow+0x22c (FPO: [Non-Fpo])
39 0012ff10 00402372 001423ba 00000001 030f0d4e SHDOCVW!IEWinMain+0x129 (FPO: [Non-Fpo])
3a 0012ff60 00402444 00400000 00000000 001423ba iexplore!WinMainT+0x2de (FPO: [Non-Fpo])
3b 0012ffc0 7c817067 030f0d4e 0007d880 7ffd9000 iexplore!_ModuleEntry+0x99 (FPO: [Non-Fpo])
3c 0012fff0 00000000 00402451 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

让咱先看看0x12-0x26这几层:

12 0012c38c 77157d50 00000000 00000034 00000501 OLEAUT32!COlePropertySheet::DoModal+0x2a (FPO: [Non-Fpo])
13 0012c3ec 7e5a6806 0012c414 7e25689c 7e558bc0 OLEAUT32!OleCreatePropertyFrameIndirect+0x28 (FPO: [Non-Fpo])
14 0012c494 7e5a6adc 002dc857 00000000 00000000 SHDOCVW!CDocHostUIHandler::ShowPropertysheetDialog+0x1f9 (FPO: [Non-Fpo])
15 0012c4a8 7e58bf11 00151808 7e25688c 0000000a SHDOCVW!CDocHostUIHandler::Exec+0xab (FPO: [Non-Fpo])
16 0012d74c 7e57ff44 0017782c 7e25688c 0000000a SHDOCVW!CDocObjectHost::OnExec+0xae2 (FPO: [Non-Fpo])
17 0012d77c 7e355e07 00000000 7e25688c 0000000a SHDOCVW!CDocObjectHost::Exec+0x101 (FPO: [Non-Fpo])
18 0012d8b4 7e315ec5 01f407f0 0004010a 00000000 mshtml!CDoc::ShowPropertyDialog+0x10b (FPO: [Non-Fpo])
19 0012dc4c 7e29bba5 00000000 7e55890c 0000001c mshtml!CDoc::ExecHelper+0x252 (FPO: [Non-Fpo])
1a 0012dc6c 7e5a4960 001c4af8 7e55890c 0000001c mshtml!CDoc::Exec+0x1e (FPO: [Non-Fpo])
1b 0012dd58 7e5aa6b7 00151800 0000001c 0012ddb4 SHDOCVW!CDocHostUIHandler::ShowContextMenu+0x436 (FPO: [Non-Fpo])
1c 0012dd84 7e395765 00177810 00000000 0012ddb4 SHDOCVW!CDocObjectHost::ShowContextMenu+0x88 (FPO: [Non-Fpo])
1d 0012ddc8 7e36cf18 0000017b 0000013a 00000000 mshtml!CDoc::ShowContextMenu+0xee (FPO: [Non-Fpo])
1e 0012dde0 7e36cee2 0000017b 0000013a 00000000 mshtml!CElement::ShowContextMenu+0x1b (FPO: [Non-Fpo])
1f 0012deec 7e330067 0000013a 0000013a 00000000 mshtml!CElement::OnContextMenu+0xb0 (FPO: [Non-Fpo])
20 0012dfa4 7e2f6e72 02061f00 0012e1c8 0205ddc0 mshtml!CLayout::HandleMessage+0x2b9 (FPO: [Non-Fpo])
21 0012e088 7e2f69c9 02061f00 0012e1c8 0205de40 mshtml!CFlowLayout::HandleMessage+0x64f (FPO: [Non-Fpo])
22 0012e0a4 7e2f6647 0205ddc0 0012e1c8 0205de40 mshtml!CElement::HandleMessage+0x90 (FPO: [Non-Fpo])
23 0012e120 7e2e8cfa 0205de40 0205de40 00000000 mshtml!CDoc::PumpMessage+0x93c (FPO: [Non-Fpo])
24 0012e26c 7e2e7c78 0000007b 00000000 013a017b mshtml!CDoc::OnMouseMessage+0x3b6 (FPO: [Non-Fpo])
25 0012e38c 7e278a1a 00000000 0000007b 0004010a mshtml!CDoc::OnWindowMessage+0xc43 (FPO: [Non-Fpo])
26 0012e4c4 77d18734 0004010a 0000007b 0004010a mshtml!CServer::WndProc+0x9f (FPO: [Non-Fpo])

0x26 - 0x1b是接收到用户点击右键,然后弹出右键菜单的过程。 0x1a - 0x14是弹出属性框的过程,0x13 - 0x12则是属性框的操作了。所以,我们需要的是什么大家也都知道了。

shdocvw,这个dll的作用是:
f1.png

只是为了加个水印。

好吧,差不多就是这样的结构(这个是IE5/6的,看图片就知道有年头了)
f2.png

shdocvw从虚拟机里面拖出来,IDA看一下这个函数的原型是什么:
CDocHostUIHandler::ShowPropertysheetDialog(tagVARIANT *,tagVARIANT *,ulong)

好吧,其实事实上就是:

HRESULT
CDocHostUIHandler::ShowPropertysheetDialog(VARIANTARG *pvarargIn, VARIANTARG *pvarargOut, DWORD)
{

// must match order of PropertysheetEnum
static const SExpandoInfo s_aPropertysheetExpandos[] =
{
    {OLESTR("propertysheetPunks"),  VT_SAFEARRAY}
};

    HRESULT             hr;
    HWND                hwnd = NULL;
    HWND                hwndParent;
    IUnknown          * punk = NULL;
    OLECMD              olecmd = {0, 0};
    int                 cUnk = 0;
    IUnknown * HUGEP  * apUnk = NULL;
    OCPFIPARAMS         ocpfiparams;
    CAUUID              ca = { 0, 0 };
    RECT                rc = {0, 0, 0, 0};
    RECT                rcDesktop = {0, 0, 0, 0};
    SIZE                pixelOffset;
    SIZE                metricOffset = {0, 0};

    IHTMLEventObj     * pEventObj = NULL;
    const int           cExpandos = ARRAYSIZE(s_aPropertysheetExpandos);
    VARIANT             aVariant[cExpandos];
    DISPID              aDispid[cExpandos];
    SAFEARRAY         * psafearray = NULL;

    ASSERT(pvarargIn && V_VT(pvarargIn) == VT_UNKNOWN && V_UNKNOWN(pvarargIn));
    if (!pvarargIn || (V_VT(pvarargIn) != VT_UNKNOWN) || !V_UNKNOWN(pvarargIn))
    {
        hr = E_INVALIDARG;
        goto Cleanup;
    }

    // get the hwnd
    punk = V_UNKNOWN(pvarargIn);
    hr = GetHwndFromUnknown(punk, &hwnd);
    if (hr)
        goto Cleanup;

    // get the SafeArray expando from the event obj
    hr = GetEventFromUnknown(punk, &pEventObj);
    if (hr)
        goto Cleanup;

    hr = GetParamsFromEvent(
            pEventObj,
            cExpandos,
            aDispid,
            aVariant,
            s_aPropertysheetExpandos);
    if (hr)
        goto Cleanup;
    psafearray = V_ARRAY(&aVariant[PropertysheetPunks]);

    // verify array dimensions
    if (SafeArrayGetDim(psafearray) != 1)
    {
        hr = E_INVALIDARG;
        goto Cleanup;
    }

    // get array size, adding one to 0-based size
    hr = SafeArrayGetUBound(psafearray, 1, (long*)&cUnk);
    if (hr)
        goto Cleanup;
    cUnk++;

    if (cUnk)
    {
        // get pointer to vector
        hr = SafeArrayAccessData(psafearray, (void HUGEP* FAR*)&apUnk);
        if (hr)
            goto Cleanup;
    }
    else
    {
        cUnk = 1;
        apUnk = &punk;
    }

    // Compute pages to load
    hr = THR(GetCommonPages(cUnk, apUnk, &ca));
    if (hr)
        goto Cleanup;

    //  compute top-level parent
    while ( hwndParent = GetParent(hwnd) )
        hwnd = hwndParent;

    // BUGBUG dialog box is not centered on screen
    // the ocpfi seems to be ignoring the x, y values in ocpfiparams
    // Compute offset to center of screen
    GetWindowRect(GetDesktopWindow(), &rcDesktop);
    GetWindowRect(hwnd, &rc);
    pixelOffset.cx = (rcDesktop.right - rcDesktop.left)/2 - rc.left;
    pixelOffset.cy = (rcDesktop.bottom - rcDesktop.top)/2 - rc.top;
    AtlPixelToHiMetric(&pixelOffset, &metricOffset);

    memset(&ocpfiparams, 0, sizeof(ocpfiparams));

    ocpfiparams.cbStructSize = sizeof(ocpfiparams);
    ocpfiparams.hWndOwner = hwnd;
    ocpfiparams.x = metricOffset.cx;
    ocpfiparams.y = metricOffset.cy;
    ocpfiparams.lpszCaption = NULL;
    ocpfiparams.cObjects = cUnk;
    ocpfiparams.lplpUnk = apUnk;
    ocpfiparams.cPages = ca.cElems;
    ocpfiparams.lpPages = ca.pElems;
    ocpfiparams.lcid = GetUserDefaultLCID();
    ocpfiparams.dispidInitialProperty = DISPID_UNKNOWN;

    // OleCreatePropertyFrameIndirect throws its own dialog on error,
    // so we don't want to display that twice
    THR(OleCreatePropertyFrameIndirect(&ocpfiparams));
    hr = S_OK;

Cleanup:
    if (ca.cElems)
        CoTaskMemFree(ca.pElems);

    if (psafearray && apUnk)
        SafeArrayUnaccessData(psafearray);

    if (pvarargOut)
        VariantInit(pvarargOut);

    for (int i=0; i<cExpandos; i++)
        VariantClear(&aVariant[i]);

    ATOMICRELEASE(pEventObj);

    return hr;
}

读一下代码,看看它做了什么:
1、pvarargIn的作用仅仅是让它获取一个hwnd。

punk = V_UNKNOWN(pvarargIn);
hr = GetHwndFromUnknown(punk, &hwnd);

2、从pEventObj中获取属性页的各个参数

hr = GetParamsFromEvent(
        pEventObj,
        cExpandos,
        aDispid,
        aVariant,
        s_aPropertysheetExpandos);

3、从这些参数中获取Common Page,待会儿就能知道Common Pages是什么

 hr = THR(GetCommonPages(cUnk, apUnk, &ca));
if (hr)
    goto Cleanup;

4、计算窗口所需参数,弹出窗口

THR(OleCreatePropertyFrameIndirect(&ocpfiparams));

现在让我们看看重要的GetCommonPages,它的作用是获取页面的属性。

HRESULT CDocHostUIHandler::GetCommonPages(int cUnk, IUnknown **apUnk, CAUUID *pca)
{
    HRESULT                hr;
    int                    i;
    UINT                   iScan, iFill, iCompare;
    BOOL                   fFirst = TRUE;
    CAUUID                 caCurrent;
    IUnknown *             pUnk;
    ISpecifyPropertyPages *pSPP;

    pca->cElems = 0;
    pca->pElems = NULL;

    for (i = 0; i < cUnk; i++)
    {
        pUnk = apUnk[i];
        ASSERT(pUnk);

        hr = THR(pUnk->QueryInterface(
                IID_ISpecifyPropertyPages,
                (void **)&pSPP));
        if (hr)
            goto Cleanup;

         hr = THR(pSPP->GetPages(fFirst ? pca : &caCurrent));
         ATOMICRELEASE(pSPP);
         if (hr)
             goto Cleanup;

         if (fFirst)
             continue;

         // keep only the common pages
         else
         {
             for (iScan = 0, iFill = 0; iScan < pca->cElems; iScan++)
             {
                 for (iCompare = 0; iCompare < caCurrent.cElems; iCompare++)
                 {
                     if (caCurrent.pElems[iCompare] == pca->pElems[iScan])
                         break;
                 }
                 if (iCompare != caCurrent.cElems)
                 {
                     pca->pElems[iFill++] = pca->pElems[iScan];

                 }
             }
             pca->cElems = iFill;
             CoTaskMemFree(caCurrent.pElems);
         }
    }


Cleanup:
    return hr;
}

可见,apUnk很可能就是当前页面的IUnknown,所以,也就是说我们完全可能从当前页面获取到这些属性!让我们在MSDN上查询一下IID_ISpecifyPropertyPages,

ISpecifyPropertyPages interface

Indicates that an object supports property pages. OLE property pages enable an object to display its properties in a tabbed dialog box known as a property sheet. An end user can then view and change the object's properties. An object can display its property pages independent of its client, or the client can manage the display of property pages from a number of contained objects in a single property sheet. Property pages also provide a means for notifying a client of changes in an object's properties.

Minimum supported client
    Windows 2000 Professional [desktop apps only]

Minimum supported server
    Windows 2000 Server [desktop apps only]

Header
    OCIdl.h

IDL
    OCIdl.idl

IID
     IID_ISpecifyPropertyPages is defined as B196B28B-BAB4-101A-B69C-00AA00341D07

这样就好办得多了,至少我们知道了如下信息:

1、属性框确实是COM对象的属性,而且不是在IE中画的,是通过ISpecifyPropertyPages接口弹出来的东西。
2、我们从IUnknown中就可能能检索出所有的属性。

have a try,后续试验在(b)篇。

看看AxMan的源代码是怎么获取COM组件的信息的

主要看AxMan枚举所有ActiveX的属性的地方。

是个坑,但是偶尔也会填填。

1、如何确定机器上有哪些ActiveX已经装上了?

很明智的做法:

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Classes\\CLSID"), 0, KEY_READ, &hKey);
    RegQueryInfoKey(
        hKey, // key handle 
        NULL, // buffer for class name 
        NULL, // size of class string 
        NULL, // reserved 
        &cSubKeys, // number of subkeys 
        NULL, // longest subkey size 
        NULL, // longest class string 
        NULL, // number of values for this key 
        NULL, // longest value name 
        NULL, // longest value data 
        NULL, // security descriptor 
        NULL  // last write time 
    );

    for (j = 0; j<cSubKeys; j++)
    {
        cbName=MAX_PATH;
        achKey[0] = '\0';
        RegEnumKeyEx(hKey, j,
            achKey, 
            &cbName, 
            NULL, 
            NULL, 
            NULL, 
            &ftime); 
        _ftprintf(output, _T("\t'%s'%s\n"), achKey, j == (cSubKeys-1) ? _T("") : _T(","));
        fflush(output);
        scan_clsid(achKey);
    }

针对找到的每个CLSID主键都调用一次“自己 GO {CLSID}”,很明智的递归方式,因为我发现在枚举的时候它就崩了无数次了。搁在一个进程里面估计这程序就完了。

2、看看GO CLSID的时候它做了啥:
(a)先设置一个异常处理程序,以便出错的时候做报告。 虽然我一次都没见它报告过。

(b)调用CLSIDFromString将传来的CLSID转为CLSID类型,简单来说就是转成这样:

typedef struct {
    unsigned long  Data1;
    unsigned short Data2;
    unsigned short Data3;
    byte           Data4[ 8 ];
} GUID;

(c)读取HKLM\SOFTWARE\CLASSES\CLSID{SOME-CLSID}\的默认值,通常它像是:

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{00000000-12C9-4305-82F9-43058F20E8D2}
(默认)    QQDownload IE Left Helper   REG_SZ

是的,这里有描述。

(d)再读HKLM\SOFTWARE\CLASSES\CLSID{SOME-CLSID}\ProgId的默认值,通常它的值像是:
还是上面的例子,

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{00000000-12C9-4305-82F9-43058F20E8D2}\ProgID
(默认)  QQIEHelper.QQCycloneHelper.1   REG_SZ

对,就是ProgId,字面意思。

(e)再读HKLM\SOFTWARE\CLASSES\CLSID{SOME-CLSID}\InProc32的默认值,通常它就是要加载的DLL,至于ThreadingModel,大家不是都默认Both或者Apartment了吗,它也没管。

(f)然后就到了最重要的部分,它怎么枚举属性和方法?
[I] 先CoCreateInstance创建一个实例,查询其IObjectSecurity接口;
[II] 如果实现了这个接口,查询是否设置了Safe for init和Safe for script位,这个是它待会儿要写到测试的配置里面去的;
[III] 调用IDispatch中的GetTypeInfo获取ITypeInfo接口用来展开相关的内容;

The ITypeInfo interface provides access to the following: 
 The set of function descriptions associated with the type. For interfaces, this contains the set of member functions in the interface.

 The set of data member descriptions associated with the type. For structures, this contains the set of members of the type.

 The general attributes of the type, such as whether it describes a structure, an interface, and so on.

简单的说就是ITypeInfo接口提供了对这个接口中的成员函数、成员变量、通用属性(是否定义了一个结构体,一个接口等等)。

[IV] 对TypeInfo调用GetDocumentation获取函数数量,然后对各个函数调用GetFuncDesc获取函数描述,然后这里就可以获得函数名,返回值,参数数量和参数。

COM接口实现的随意的一点小记录

1、所有的COM接口的vtbl前三项都是一样的。
COM接口和C++编译器生成的抽象基类的内存接口是相同的。
vtbl的初始位置保存着IUnknown的御三家的地址,take an example:

在VS里面的例子,实例化IWebBrowser2之后,查看其地址的数据:

>dd 0x00821ae8
0x00821AE8  63362618(vtbl) 005d25a8 005d87fc abababab  
0x00821AF8  abababab       feeefeee 00000000 00000000  
0x00821B08  45b71a23       00001234 008237f8 008200c4  
0x00821B18  feeefeee       feeefeee feeefeee feeefeee  

f2.png

很容易就能找到御三家。

示例:

#include <ObjBase.h>
#include <iostream>

static IID IID_IA = {0x12345678, 0x1234, 0x1234, {0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12}};

interface IA : IUnknown
{
    virtual void __stdcall foo() = 0;
};

class CA : public IA
{
public:
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
    virtual ULONG __stdcall AddRef(){std::cout<<"addref\n"<<std::endl; return 0;}
    virtual ULONG __stdcall Release(){std::cout<<"release\n"<<std::endl; return 0;}
    virtual void __stdcall foo()
    {
        std::cout << "a" << std::endl;
    }
};

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{
    if (iid == IID_IUnknown || iid == IID_IA)
    {
        *ppv = static_cast<IA*>(this);
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

IUnknown* CreateInstance()
{
    IUnknown* pI = static_cast<IA*>(new CA);
    pI->AddRef();
    return pI;
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    IUnknown* pIUnknown = CreateInstance();

    IA* pIA = NULL;
    hr = pIUnknown->QueryInterface(IID_IA, (void**)&pIA);
    if (SUCCEEDED(hr))
    {
        pIA->foo();

    printf("%08x %08x %08x",*(DWORD*)pIA, 
        *((DWORD*)pIA+1), 
        *((DWORD*)pIA+2));
    }   

    return 0;
}

输出:
00a07834 fdfdfdfd abababab

其实事实上pIA此时内存也就这一个东西了,拿出VS的dd证明一下代码没错:

>dd 0x007c8c38
0x007C8C38  00a07834(vtbl) fdfdfdfd abababab abababab  
0x007C8C48  00000000 00000000 18fb7b76 1c00dcbf  
0x007C8C58  007c8c18 007c4810 0f9a22b0 0000041d  
0x007C8C68  00000010 00000002 000000ba fdfdfdfd 

00a07834解引用后,前三项为QueryInterface、AddRef、Release的地址。

跟这关系不大的一个,在添加了私有成员之后:

class CA : public IA
{
public:
………………
private:
    ULONG m_SomeLong;
};

vtbl后面才会多出数据来,以前只是看书知道这事儿,这回算是实践了吧:

{,,testInterface.exe}(*(CA*){*}pIA).m_SomeLong  3452816845  unsigned long

>dd 0x00888c38
0x00888C38  00377834 *cdcdcdcd* fdfdfdfd abababab  
0x00888C48  abababab feeefeee 00000000 00000000  
0x00888C58  0d743d6b 1c0082e0 00888c18 00884810  
0x00888C68  0fbf22b0 0000041d 00000010 00000002  

(备注: 3452816845 (dec) == 0xcdcdcdcd (hex))

2、IDispatch接口中GetIDsOfNames根据提供的函数名称返回DISPID,Invoke则内部实现了一组按索引访问的函数,有点像是vtbl。

春节14天小长假,该补的还是全部补上吧

去年最后悔的就是把一堆没看完的书打着“放假我能看完”的名号带回家里,回北京以后想看书的时候一摸啥都没有,这14天好好的补完吧。我可能会尽量先补上ie的那几篇文章的坑,开坑的时候,坑挖的太大了,趁着过节给填上。

fswebcam 实时监控系统

f1.png

cubietruck搭出来的,不管有没有用。。每张图片~200k,每天监控下来的数据差不多有1.7GB,正好有个空闲的SSD,120G空间足以监控10天,其实监控三个月都不为过。。

然后给它开启samba和vnc,顺便backup还把图片传到网站的某个文件夹里面,网站空间很小,所以我只能15分钟传一张,每天产生20M数据,10天200M,看起来还可以的样子。

其实晚上拍的照片一片黑 每张也就17k左右