首页
社区
课程
招聘
3
[原创]逆向IE11的首页保护算法
发表于: 2015-1-12 21:22 19042

[原创]逆向IE11的首页保护算法

2015-1-12 21:22
19042

一、  概述
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直播授课

上传的附件:
收藏
免费 3
支持
分享
赞赏记录
参与人
雪币
留言
时间
飘零丶
为你点赞~
2024-5-31 04:25
shinratensei
为你点赞~
2024-5-31 04:13
PLEBFE
为你点赞~
2023-3-1 03:11
最新回复 (13)
雪    币: 188
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
楼主好牛叉啊
2015-1-12 22:12
0
雪    币: 324
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark一下,手机客户端看不清图
2015-1-13 10:13
0
雪    币: 2105
活跃值: (569)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
楼主厉害啊
2015-1-13 10:44
0
雪    币: 135
活跃值: (63)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
不明觉厉。马克。
2015-1-13 10:50
0
雪    币: 143
活跃值: (263)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
6
2345 根本就不管你注册表, 就算是改成 默认为空, 或者新标签 , 照样转到他的网址
2015-1-25 15:37
0
雪    币: 1258
活跃值: (1439)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
好像,很厉害的样子。
2015-1-25 17:33
0
雪    币: 152
活跃值: (106)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
8
不错不错:)
2015-1-26 10:31
0
雪    币: 1522
活跃值: (2308)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
楼主威武!!!
2015-1-26 11:27
0
雪    币: 8
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
直接修改start page,IE11会提示有位置程序要更改主页,但是用360去修改,则没有提示。360主页守护开启以后,修改start page;修改组策略--禁止更改主页,都无效,不知道怎么搞的,,,
2015-11-27 11:13
0
雪    币: 43
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
太厉害了,我分析不动!!!佩服楼主
2015-12-16 13:17
0
雪    币: 244
活跃值: (189)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
俺的主页曾经被改过,结果改回来后死活剩下http:///囧
2017-6-16 13:53
0
雪    币: 2657
活跃值: (4222)
能力值: ( LV3,RANK:32 )
在线值:
发帖
回帖
粉丝
13
注册表第三段长度是0x14的值是证书指纹,他的值就在证书管理中的企业信任证书中IE Enhanced User Preference Protection中的指纹对应的值。
2020-10-10 12:21
0
雪    币: 41
活跃值: (269)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
这样的分析过程,十分可贵。
2020-10-10 15:50
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册