leonwxqian 发布的文章

PHP 获取EXE的版本信息

via http://stackoverflow.com/questions/2029409/ 有修改,Linux服务器也可用,Windows服务器用FSO对象就可以了,更简单。

function GetFileVersion($FileName) {

$handle=fopen($FileName,'rb');
if (!$handle) return FALSE;
$Header=fread ($handle,64);
if (substr($Header,0,2)!='MZ') return FALSE;
$PEOffset=unpack("V",substr($Header,60,4));
if ($PEOffset[1]<64) return FALSE;
fseek($handle,$PEOffset[1],SEEK_SET);
$Header=fread ($handle,24);
if (substr($Header,0,2)!='PE') return FALSE;
$Machine=unpack("v",substr($Header,4,2));
if ($Machine[1]!=332) return FALSE;
$NoSections=unpack("v",substr($Header,6,2));
$OptHdrSize=unpack("v",substr($Header,20,2));
fseek($handle,$OptHdrSize[1],SEEK_CUR);
$ResFound=FALSE;
for ($x=0;$x<$NoSections[1];$x++) {
    $SecHdr=fread($handle,40);
    if (substr($SecHdr,0,5)=='.rsrc') {         //resource section
        $ResFound=TRUE;
        break;
    }
}
if (!$ResFound) return FALSE;
$InfoVirt=unpack("V",substr($SecHdr,12,4));
$InfoSize=unpack("V",substr($SecHdr,16,4));
$InfoOff=unpack("V",substr($SecHdr,20,4));
fseek($handle,$InfoOff[1],SEEK_SET);
$Info=fread($handle,$InfoSize[1]);
$NumDirs=unpack("v",substr($Info,14,2));
$InfoFound=FALSE;
for ($x=0;$x<$NumDirs[1];$x++) {
    $Type=unpack("V",substr($Info,($x*8)+16,4));
    if($Type[1]==16) {                          //FILEINFO resource
        $InfoFound=TRUE;
        $SubOff=unpack("V",substr($Info,($x*8)+20,4));
        break;
    }
}
if (!$InfoFound) return FALSE;
$SubOff[1]&=0x7fffffff;
$InfoOff=unpack("V",substr($Info,$SubOff[1]+20,4)); //offset of first FILEINFO
$InfoOff[1]&=0x7fffffff;
$InfoOff=unpack("V",substr($Info,$InfoOff[1]+20,4));    //offset to data
$DataOff=unpack("V",substr($Info,$InfoOff[1],4));
$DataSize=unpack("V",substr($Info,$InfoOff[1]+4,4));
$CodePage=unpack("V",substr($Info,$InfoOff[1]+8,4));
$DataOff[1]-=$InfoVirt[1];
$Version=unpack("v4",substr($Info,$DataOff[1]+48,8));
$x=$Version[2];
$Version[2]=$Version[1];
$Version[1]=$x;
$x=$Version[4];
$Version[4]=$Version[3];
$Version[3]=$x;
return $Version;
}

执行程序,例如我需要的是curl上传一个文件,有这个就不用喊管理员开curl了:

exec 
(PHP 4, PHP 5)
exec — Execute an external program


Description 
string exec ( string $command [, array &$output [, int &$return_var ]] )
exec() executes the given command. 

IE的FREAK对策(c)

接着之前的来,首先我们把AxMan的相关代码拖出来整理一下,由于我们现在有IDispatch了,所以我们先试验一下,通过精简版的AxMan遍历代码得到了所有的属性,大概有400个:

[374]Name 的值是 'createRange';
[374]Return 的值是 '????慒杮?';
[374]ArgCount 的值是 0;

[375]Name 的值是 'addEventListener';
[375]Return 的值是 '潶摩??慒杮?';
[375]ArgCount 的值是 3;

[376]Name 的值是 'removeEventListener';
[376]Return 的值是 '潶摩??慒杮?';
[376]ArgCount 的值是 3;

[377]Name 的值是 'dispatchEvent';
[377]Return 的值是 '??乁彔佂??';
[377]ArgCount 的值是 1;

[378]Name 的值是 'rootElement';
[378]Return 的值是 '卉?噓?敬敭瑮*';
[378]ArgCount 的值是 0;

可是最后似乎还是有点问题,信息中出现了很多乱码。而且也没有连接的信息,大部分还是一些无关痛痒甚至js就能拿到的东西。

f2.gif
最后,我查阅了资料发现IPersistPropertyPage是可以再Query出一个IHTMLDocument2的,这个就是证书那个属性页的具体内容。也是iframe.dll的一个资源,具体怎么实现的你们一查便知。

else if (elm.id == "_connInfo" )
{
var secInfo = window.dialogArguments.secureConnectionInfo;
if (secInfo == "")
elm.innerText = L_NotEncrypted_Text;
else
elm.innerText = secInfo ;
}

最后,我发现IHTMLOptionsHolder这个接口有个secureConnectionInfo,按照描述,就是它返回了连接的安全信息。
f1.gif

最后,我们根本没用这个,因为,IHTMLOptionsHolder根本获取不到! ←_←

最后,另一位大神给了另一种方式,两个都比较简单有效。大神的方法是挂Wininet的函数,我这边用的是Schannel的函数,最后效果大同小异,IE上防御不成问题。感谢大神在这个问题上给了无尽的思路。←_←
f3.gif

IE的FREAK对策(b)

接着之前的来,我们先自己让WebBrowser弹一下属性:

void CWebBrowserView::OnNavigateComplete2(LPCTSTR strURL)
{
    // TODO: 在此添加专用代码和/或调用基类

    LPDISPATCH pIDispatch = this->GetHtmlDocument();
    if (!pIDispatch)
        goto Exit0;

    ISpecifyPropertyPages  *pISPP;    
    CAUUID                  caGUID;   
    HRESULT                 hr;    
    if (FAILED(pIDispatch->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pISPP)))
    {        
        MessageBox(TEXT("Object has no property pages."));        
        goto Exit0;        
    }   
    hr=pISPP->GetPages(&caGUID);    
    pISPP->Release();    
    if (FAILED(hr))
    {        
        MessageBox(TEXT("Failed to retrieve property page GUIDs."));
        goto Exit0;       
    }    

    hr = OleCreatePropertyFrame(m_hWnd, 10, 10, NULL , 1, (IUnknown **)&pIDispatch, caGUID.cElems  , caGUID.pElems, 0x0804, -1L, 0);   
    if (FAILED(hr))       
        MessageBox(TEXT("OleCreatePropertyFrame failed."));    //Free GUIDs.    
    CoTaskMemFree((void *)caGUID.pElems);    

Exit0:
    CHtmlView::OnNavigateComplete2(strURL);
}

OK,这时属性窗口弹出来了,然后,我们看一下比较重要的cElem,它的里面仅仅存放着一个内容,即Microsoft Document Browse Property Page:

-       caGUID  {cElems=1 pElems=0x04e6f3d8 }   tagCAUUID
        cElems  1   unsigned long
-       pElems  0x04e6f3d8 {CLSID_%Microsoft Document Browse Property Page%}    _GUID *
        Data1   810611636   unsigned long
        Data2   39093   unsigned short
        Data3   4559    unsigned short
+       Data4   0x04e6f3e0 "粋"  unsigned char [8]

要说怎么微软的IE大家开发起来都觉得烦人呢,主要就是缺资源,你说Microsoft Document Browse Property Page这个是IE的Property Bag没错,但是里面的值怎么解释呢?

它的GUID定义如下:

// {3050f3B4-98b5-11cf-bb82-00aa00bdce0b}
DEFINE_GUID(CLSID_CDocBrowsePropertyPage, 0x3050f3B4, 0x98b5, 0x11cf, 0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b);

然后,让我们看看,caGUID的Data字段,确实是它的guid,例如810611636 == 0x3050F3B4。既然它是一个Property Page,那么必然可以通过IPropertyPages这个方式得到它的具体内容:

A property page object manages a particular page within a property sheet. A property page implements at least IPropertyPage and can optionally implement IPropertyPage2 if selection of a specific property is supported.

事实上,按照这个思路来,我们的第一步成功了

CAUUID pages;
CComPtr<IPropertyPage> pPropertyPage;

hr = ::CoCreateInstance(caGUID.pElems[0], NULL, CLSCTX_ALL , 
    IID_IPropertyPage, (void **)&pPropertyPage);

PROPPAGEINFO pageInfo;
if (SUCCEEDED(hr))
{
    memset(&pageInfo, 0, sizeof(PROPPAGEINFO));
    pageInfo.cb = sizeof(PROPPAGEINFO);

    hr = pPropertyPage->GetPageInfo(&pageInfo);

}


-       pageInfo    {cb=28 pszTitle=0x05765600 "常规" size={...} ...} tagPROPPAGEINFO
        cb  28  unsigned long
+       pszTitle    0x05765600 "常规" wchar_t *
+       size    {cx=378 cy=414 }    tagSIZE
+       pszDocString    0x00000000 <错误的指针>  wchar_t *
+       pszHelpFile 0x00000000 <错误的指针>  wchar_t *
        dwHelpContext   0   unsigned long

f1.png

我们获得了“常规”这一页。下一步呢?目标当然是要获取这一页内部的数据了。

The IPropertyPage interface has these methods.
Method  Description
Activate    Creates the dialog box window for the property page.
Apply           Applies the current values to the underlying objects associated with the property page as previously passed to IPropertyPage::SetObjects.
Deactivate  Destroys the window created in IPropertyPage::Activate.
GetPageInfo     Retrieves information about the property page.
Help            Invokes the property page help in response to an end-user request.
IsPageDirty     Indicates whether the property page has changed since it was activated or since the most recent call to IPropertyPage::Apply.
Move            Positions and resizes the property page dialog box within the frame.
SetObjects  Provides the property page with an array of pointers to objects associated with this property page.
SetPageSite     Initializes a property page and provides the page with a pointer to the IPropertyPageSite interface through which the property page communicates with the property frame.
Show            Makes the property page dialog box visible or invisible.
TranslateAccelerator     Passes a keystroke to the property page for processing.

好景不长,似乎在IPropertyPage的方法中并没有发现任何GetObjects的方式,取而代之的只有SetObjects。查阅文档后发现,其实我们瞄准的应该是

IPerPropertyBrowsing interface

Retrieves the information in the property pages offered by an object.

所以目标明确,通过这个接口查询一下是否可以获得宝贵的连接信息。

CComPtr<IPerPropertyBrowsing>  pIPPB;
hr = pIDispatch->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pIPPB);

这样即可获得IPerPropertyBrowsing接口,IPerPropertyBrowsing接口的方法如下:

interface IPerPropertyBrowsing : IUnknown 
  { 
    HRESULT GetDisplayString([in] DISPID dispID, [out] BSTR *pbstr); 
    HRESULT MapPropertyToPage([in] DISPID dispID, [out] CLSID *pclsid); 
    HRESULT GetPredefinedStrings([in] DISPID dispID, [out] CALPOLESTR *pcaStringsOut, [out] CADWORD *pcaCookiesOut); 
    HRESULT GetPredefinedValue([in] DISPID dispID, [in] DWORD dwCookie, [out] VARIANT *pvarOut); 
  } 

微软的MSDN也表示:

To specify its support for such capabilities, the object implements IPerPropertyBrowsing. Through this interface, the caller can request the information necessary to achieve the browsing, such as predefined strings (GetPredefinedStrings) and values (GetPredefinedValue) as well as a display string for a given property (GetDisplayString).

参考ATL的说明,其实Page里面的是一大堆的COM对象,也就是说还是需要了解他们各自的CLSID是多少,这个真是相当烦人,IPerPropertyBrowsing也是无一例外的第一个参数就是DISPID。

IPerPropertyBrowsing的实现其实是类似switch DISPID,然后返回对应的值,这个在IE的右键属性还是可以看出来的,因为根据系统语言不同,它返回的内容也是不同。这个东西的用处就体现在这里了,我们可以将连接的DISPID传给它,然后获取和当前用户环境相关的描述字符,这个可以用作提示用。

在春节发的68.html中,我曾经写了:

然后就到了最重要的部分,它怎么枚举属性和方法?
[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接口提供了对这个接口中的成员函数、成员变量、通用属性(是否定义了一个结构体,一个接口等等)。

也就是说,我们用同样的方法,不出意外也是可以获取到所有属性的,让我们试一试,

参考资料
https://msdn.microsoft.com/en-us/library/c74wfbxc.aspx
https://msdn.microsoft.com/en-us/library/ms686577.aspx
这两篇有空我也翻译出来。

图:被火狐拒绝的连接
SOI4P3~L.PNG

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获取函数描述,然后这里就可以获得函数名,返回值,参数数量和参数。