一、 概述
Win7 x64
IE11版本:11.0.9600.17501 更新版本:11.0.15 KB3008923
IE11把首页的设置放在注册表里:
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\EUPP
采用了混淆字符串和证书保护。本文主要逆向其混淆字符串部分。
由于是IE的PE文件是x64版本。OD无法调试,用windbg动态分析,IDA静态分析。
Windbg和IDA都能很好地使用微软符号文件。因此定位关键的函数非常方便。但Windbg相比OD,很难用。当然主要还是不熟悉不顺手。
IDA版本是6.1,对x64的PE,没有F5。这比较痛。
逆向的过程就是在浩如烟海的数据和信息中,找出关键信息及其逻辑关系。这要求极为严谨的态度和细节处理。把目标拆小是一个非常科学的办法,每次跟踪一个数据的来龙去脉。一个点一个点地突破。
二、 主要函数调用流程:
写入注册表的过程
CBrowserFrame::_FrameMessagesWndProc
CHomePageProtector__HandleAsyncActionCallback
CHomePageProtector::HandleAsyncActionCallback
CHomePageProtector::_CommitChange
CHomepageFSM::Commit
CVerifiableHomepage::Save
IEFRAME!SetDWORD
CVerifiableHomepage::Save
StrUtil::CreateBSTRFromString
CVHPSerializer::SaveToBuffer
Encoding::ObfuscateString
Encoding::UnicodeToUtf8
Encoding::ObfuscateData
BufferUtil::StoreTLV //三次,第一次是混淆后的URL
BufferUtil::StoreBuffer //三次,第一次是编号,第二次是长度,第三次是数据。
__imp_memcpy_s //内部调用memcpy来实现
BufferUtil::StoreBuffer
BufferUtil::StoreBuffer
BufferUtil::StoreTLV
BufferUtil::StoreTLV
CSyncHomepage::GetValueID
IEFRAME!SetBinary
call cs:__imp_SetValue
注册表二进制数据中,第二段和第三段(signValue和指纹)的处理过程:
int __fastcall EUPPAsyncTask(BSTR)
CHomePageProtector::TriggerAsyncAction //触发AsyncAction和上面的AsyncActionCallback回调函数。
CHomepageFSM::UpdateHomepage
CHomepageFSM::_SignVHP //signValueHomePage?
CVerifiableHomepage::GetSignHash 按URL中的Host部分取Hash? 内部有两次memcpy,组合Unicode字符串
CVerifiableHomepage::GetUrlVerificationPart
CSignatureUtility::SHA256HashData调用微软CryptHashData系列函数
CVerifiableHomepage::GetThumbprint 取指纹,注册表03段。目前是0x14长度,指纹和hash之间没有任何关系。
CEuppWebService::SignValue 注册表第2段。大约0x100长度,当注册表中没有指纹时,这里面会取一个指纹出来?
CEuppWebService::_ProcessService
CEuppWebService::_CreateXMLString
Encoding::Base64EncodeData
CryptBinaryToString
Base64Encode
ATL::CComBSTR::Length
Encoding::UnicodeToUtf8
StringCchLengthA
CEuppWebService::_HashAndAppendUrl
CEuppWebService::_RequestService 这里面调用wininet函数用post方法。
CEuppWebService::_ParseXMLString
CVerifiableHomepage::SetHomepage
CBlob::CBlob
CBlob::CBlob
读取注册表的过程:
CHomePageProtector::UpdateHomepage //更新Homepage时,要去读原来的注册表中的值。
CHomePageProtector::_CreateHomepageFSM
CHomepageFSM::CreateInstance
CHomepageFSM::_Load
CUnprotectedHomepage::Load
CVerifiableHomepage::Load
CVerifiableHomepage::Load //第二次调用Load, rcx为CVerifiableHomepage的this指针,rcx+30为指纹值。rcx+18为signValue
GetBinary
CVHPSerializer::LoadFromBuffer
BufferUtil::FetchTLV
Encoding::UnobfuscateString
三、 混淆算法

上图中黄色部分是ObfuscateData函数,其内部处理过程如下:

这个算法核心是三步:
1、 产生一个随机数做种子。
2、 将URL分解成每四个为一组,和种子做xor运算。
3、 最后几位不足四个的单独和种子做一个循环xor运算。
C语言对算法进行模拟:(可以设置注册表项,但由于没处理证书校验。会被IE11重置。)
void ObfuscateData()
{
//char * URL = "http://go.microsoft.com/fwlink/p/?LinkId=255141";
char * URL = "http://www.sina.com.cn";
char * pURL = URL;
int len = strlen(URL);
int allLen = len + 4 + 8;
BYTE * buf = new BYTE[allLen];
BYTE * pbuf = buf;
memset(buf, 0, allLen);
int randNumber = rand();
randNumber = 0x364AFA0F; //对?照?测a试º?
int * prandNumber = &randNumber;
BYTE * pByte = (BYTE *)prandNumber;
int count = len / 4;
int index = 1;
memcpy(buf, &index, 4);
index = len + 4;
memcpy(buf + 4, &index, 4);
memcpy(buf + 8, &randNumber, 4);
pbuf += 12;
//第一次循环
for (int i = 0; i<count; i++)
{
randNumber = randNumber * 0x343FD;
randNumber += 0x269EC3;
for (int j = 0; j<4; j++){
BYTE al = (BYTE)pByte[j];
al = al ^ (BYTE)pURL[0];
pbuf[0] = al;
pbuf ++;
pURL ++;
}
}
//进入第二次循环
randNumber = randNumber * 0x343FD;
randNumber += 0x269EC3;
count = len % 4;
for (int k = 0; k<count; k++){
BYTE al = (BYTE)pByte[k];
al = al ^ (BYTE)pURL[0];
pbuf[0] = al;
pbuf ++;
pURL ++;
}
HKEY hTestKey;
if( RegOpenKeyEx( HKEY_CURRENT_USER,
TEXT("Software\\Microsoft\\Internet Explorer\\EUPP"),
0,
KEY_READ | KEY_WRITE,
&hTestKey) == ERROR_SUCCESS
)
{
if(RegSetValueEx(hTestKey, TEXT("BackupHomePage"), 0, REG_BINARY,( const unsigned char *)buf, allLen)!=ERROR_SUCCESS)
{
printf("error\n");
}
}
RegCloseKey(hTestKey) ;
delete[] buf;
}
四、 对CHomepageFSM::_SignVHP函数的分析
函数第一部分主要是调用三个子函数。图中黄色的call一共有五个,关键的是三个。
CVerifiableHomepage::GetSignHash
CVerifiableHomepage::GetThumbprint
CEuppWebService::SignValue

函数第二部分主要是在取得signvalue之后,调用CVerifiableHomepage::SetHomepage。

有时候长时间做逆向,感觉累了,休息一下,思路又清晰起来。比一直趴电脑面前傻做效果更好。
一度困惑于指纹是如何产生的。休息时想起来。马上重开电脑,下断点试试。给该类的构造函数下断点。CVerifiableHomepage::CverifiableHomepage
但是,Windbg无法对构造函数下断。提示找不到符号。
沿着这个思路,总算是找到其this指针+30所指为0(偏移30就是指纹),下ba w1断点后,可以找到CVHPSerializer::LoadFromBuffer中通过读取注册表对指纹部分的填充。
新的问题产生:注册表中没有这一项时,它又如何填充?
事实是:无论注册表中有没有指纹,都会由CEuppWebService::SignValue ()函数去取指纹。综上,需要继续搞清楚的问题:
第一个问题,hash是如何取的。
第二个问题,根据hash去WebService取指纹和signValue的过程
五、 CVerifiableHomepage::GetSignHash函数的跟踪
两次memcpy后把前缀和URL连接起来。CSignatureUtility::SHA256HashData调用微软CryptHashData系列函数获取Hash值。

CSignatureUtility::SHA256HashData函数内部如下图:

注意:CSignatureUtility的构造函数里调用CryptAcquireContext。所以在上图中看不到对这个函数的调用。
函数使用c语言还原如下:
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t * url = L"S-1-5-21-3854804644-468581721-3152918291-1000b208cede-5468-459b-9c1d-591509b3da12about:Tabs"; //前缀+URL
//注意这里必须*2.
size_t len = wcslen( url ) * 2;
char * pszHash = new char[100];
SHA256HashData((BYTE *)url, len, CALG_SHA_256, (LPTSTR)pszHash);
delete [] pszHash;
system("pause");
return 0;
}
DWORD SHA256HashData(BYTE *pbData, DWORD dwDataLen, ALG_ID algId, LPTSTR pszHash)
{
DWORD dwReturn = 0;
HCRYPTPROV hProv;
//注Á¡é意°aPROV_RSA_AES参?数ºy
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
return (dwReturn = GetLastError());
HCRYPTHASH hHash;
//注Á¡é意°aCALG_SHA_256参?数ºy
if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
{
dwReturn = GetLastError();
CryptReleaseContext(hProv, 0);
return dwReturn;
}
//注Á¡é意°a数ºy据Y长¡è度¨¨。¡êUnicode的Ì?数ºy据Y
if(!CryptHashData(hHash, pbData, dwDataLen, 0))
{
dwReturn = GetLastError();
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return dwReturn;
}
DWORD dwSize = 0x20;
DWORD dwLen = sizeof(dwSize);
CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)(&dwSize), &dwLen, 0);
BYTE* pHash = new BYTE[dwSize];
dwLen = 0x20;
CryptGetHashParam(hHash, HP_HASHVAL, pHash, &dwLen,0);
lstrcpy(pszHash, _T(""));
TCHAR szTemp[3];
for (DWORD i = 0; i < dwLen; ++i)
{
wsprintf(szTemp, _T("%02X"), pHash[i]);
lstrcat(pszHash, szTemp);
}
delete [] pHash;
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return dwReturn;
}
使用vs来调试重写的函数,传入的数据是:前缀+URL,如下图:

计算后的结果:

对照windbg中的结果。两个内存窗口,左侧内存窗口的是输入数据,右下角内存窗口是计算结果。和Visual Studio中的结果一致。

六、 一些技巧:
使用Procmon.exe监视注册表,注意进程名称和PID。由于IE11是多进程的。所以调试哪一个PID是比较关键的。

Windbg中清晰地看到调用堆栈:

使用PCHunter64位版本察看进程和模块信息,从下图可以看出:
1、 双进程中父进程是x64版本的。用windbg调试它。
2、 父进程中,IEFRAME.dll是最关键的模块。核心代码都在这个dll中。PE文件约13.7M。IDA反编译要费点时间。在windbg中下断点时,要使用bu IEFRAME!***函数。
3、 IEFRAME.dll所在目录为system32,不是C:\Windows\SysWOW64。这个要注意。因为这两个目录下都有IEFRAME.dll,而版本不同。system32中的DLL是x64版本。SysWOW64下的DLL是32位版本。在使用IDA静态反汇编时,不能搞错版本。未做深入的比较,粗看两个版本的算法大致相同。但偏移地址和寄存器的使用全然不同。


下图是混淆后的值写入注册表的内容。其数据格式为:
01 长度 内容 (图中长度为0E) (内容是混淆后的URL)
02 长度 内容 (图中长度为100) (内容是SignValue)
03 长度 内容 (图中长度看不见,要往下拖滚动条才能看到) (内容是指纹)

七、 有时间可以继续搞清楚的问题:
1、 IE是如何校验SignValue和指纹的。如果注册表里没有这两个内容,IE将如何检查并自动校正。
2、 向微软的webservice请求SignValue更详细的过程。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课