VMProtect + IDA Pro 做一回强悍的加密

VMProtect是支持手动定义需要Mutation或者Virtualization的函数的,不过默认情况下,VMP显示的只有入口点一个函数和其他系统API,在无法取得有效的map文件时,还有个非常牛逼的工具可以做到这点:IDA Pro

使用IDA Pro的Products->generate map file,然后全选(如果你真的需要的话),再保存成appname.map,使用VMP编辑脚本,Add New Script,这个时候就可以……

f1.png

有时候VMP还会报错:
MAP file have incorrect timestamp and can not be loaded.

A mapfile is a text file that contains the following information about the program being linked:
    The module name, which is the base name of the file
    The timestamp from the program file header (not from the file system)
    A list of groups in the program, with each group's start address (as section:offset), length, group name, and class
    A list of public symbols, with each address (as section:offset), symbol name, flat address, and .obj file where the symbol is defined
    The entry point (as section:offset)

msdn: https://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx

IDA生成的MAP很简单,为啥会有timestamp呢,查看帮助文档后知道了,map的修改时间必须和exe一致,所以做个程序同时修改map和exe的修改时间即可~

还有一个小问题:

 Start         Length     Name                   Class
 0001:00000000 000005504H .rdata                 DATA
 0002:00000000 0000033BCH .data                  DATA
 0003:00000000 0000051E4H .rsrc                  DATA
 0004:00000000 000003D16H .reloc                 DATA

对比EXE可见少了一节,最好也手动补上:

 Start         Length     Name                   Class
 0001:00000000 000010025H .text                  CODE
 0002:00000000 000005504H .rdata                 DATA
 0003:00000000 0000033BCH .data                  DATA
 0004:00000000 0000051E4H .rsrc                  DATA
 0005:00000000 000003D16H .reloc                 DATA


  Address         Publics by Value

 0001:00000000       sub_401000
 0001:00000078       wWinMain(x,x,x,x)

Windows XP URLDownloadToFile异常失败bug一例

奇葩的问题,某个URL IE无法下载,但是用其他下载工具却能下载,我倒不在乎其他下载工具咋下的……关键是URLDownloadToFile拉不到这个文件,各种失败,这个让我比较在意,看看原因是什么。

首先,这个问题是只在Windows XP,应该是没打补丁的XP3上有,至于打补丁的情况我也没验证了,跟urlmon有关。这个是报错信息:

(ce4.9d8): Unknown exception - code 80040155 (first chance)
(ce4.84c): Unknown exception - code 80040155 (first chance)
eax=80040155

这个结果由CFileDownload::KickOffDownload返回。这个值是E_IIDNOTREG,interface not registered。这是为什么呢……感觉如此奇怪。

URLDownloadToFile的工作模式是 :创建一个CFileDownload,然后调用CFileDownload::KickOffDownload,CFileDownload派生自CBaseBSCB,CBaseBSCB::KickOffDownload会:
1、创建一个URL Moniker(IMoniker);
2、获得IBindCtx接口;
3、RegisterBindStatusCallback注册CallBack
4、将Moniker与IStream绑定,此时开始下载

BindToStorage时出错,返回REGDB_E_IIDNOTREG,原因是CTransaction::Start会检查请求的MIME Type,但是服务器蛋疼的把MIME Type设置成了……
f1.jpg

最后通知管理员解决了……

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