使用MIME filter实现JS脚本拦截并修改

一.引言

     好久没更新博客了...最近研究了一下MIMEFILTER拦截JS脚本,为什么要研究这个问题,想必看到这篇文章的你懂的... :D网上关于这个话题的资料太少了,我知道研究的辛苦,所以帖出来,希望我走过的弯路化作经验,能够帮助到需要的朋友。

 

 二.关于MIME FILTER

           MIME的英文全称是"Multipurpose Internet Mail Extensions" 多功能Internet 邮件扩充服务,它是一种多用途网际邮件扩充协议,在1992年最早应用于电子邮件系统,但后来也应用到浏览器。MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。MEMIFILTER是微软用来支持研发者扩展IE浏览器功能的一套机制,微软原文为:Asynchronous pluggable protocols enable developers to create pluggable protocol handlers, MIME filters, and namespace handlers that work with Microsoft Internet Explorer 4.0 and later and aURL moniker.由此可见,MIMEFILTERS是异步可插拔协议的一种应用方式。

        异步可插拔协议提供三种方式实现URI到类的映射(我的翻译部标准,就引用MSDN原文了):

  • Permanently registering an asynchronous pluggable protocol handler in the registry. The handler is used for any URIs with the specified scheme (such ashttp,ftp, and so on).
  • Temporarily registering a pluggable namespace handler. The handler is used for any URLs with a particular protocol scheme.
  • Permanently or temporarily registering a MIME filter. The handler manipulates the data stream it receives and returns a data stream for any resources of the specified MIME type.

        其中第三种就是永久或临时地注册一个MIME filter,处理例程对接收到的特定的MIME filter类型的数据流进行处理后然后返回处理后的数据流。因此关于MIME filter总共有两种类型,一种是永久注册,一种是临时注册。

        永久注册的MIME filter必须在注册表的HKEY_CLASSES_ROOT\PROTOCOLS\Filter\<mime_filter>位置注册;

         临时注册MIME filter则使用IInternetSession接口注册。

        IE所支持的MIME类型可查看http://msdn.microsoft.com/en-us/library/ms775147(v=vs.85).aspx,JS并不在其中,可事实上,IE是支持JS过滤的。

        JavaScript的MIME 类型有三种:   

  • text/javascript
  • application/javascript
  • application/x-javascript

怎样创建一个MIMEFILTER工程可参考微软提供的实例:http://support.microsoft.com/kb/260840

       

三. 实现JS拦截和修改

    因为JS的MIME类型有三种,如果想注册为永久型的话,则需要写三次注册表,可通过修改RGS文件实现。例如添加

NoRemove PROTOCOLS
 {
  NoRemove Filter
  {
   ForceRemove 'text/javascript' = s 'JSMimeFilter MIME Filter Sample'
   {
    val CLSID = s '{53B95211-7D77-11D2-9F80-00104B107C96}'
   }
  }
 }

就实现了'text/javascript'类型的永久注册(CLSID的值需修改为你自己创建的插件的CLSID值)。但是我发现如果我永久注册三种类型,调试的时候是拦截不到的。我自己的思路是在RGS里注册text/html类型,然后在临时注册三种JS的MIME类型。

        拦截步骤:

        1. 修改RGS文件

NoRemove PROTOCOLS
 {
  NoRemove Filter
  {
   ForceRemove 'text/html' = s 'HTMLMimeFilter MIME Filter Sample'
   {
    val CLSID = s '{53B95211-7D77-11D2-9F80-00104B107C96}'
   }
  }
 }

         2. 在新建的过滤类中添加2个成员:

 IInternetSession* m_pSession;
 IClassFactory* m_pFactory;

在构造函数中临时注册三种JS MIME类型:

 HRESULT hr = CoGetClassObject(CLSID_HttpContentFilterPP, CLSCTX_SERVER, NULL, IID_IClassFactory, (void**)&m_pFactory);
 if(hr==S_OK)
 {
  if(CoInternetGetSession(0, &m_pSession, 0)==S_OK)
  {
   //临时注册其它三种类型
   hr = m_pSession->RegisterMimeFilter(m_pFactory, CLSID_HttpContentFilterPP, L"application/javascript");
   hr = m_pSession->RegisterMimeFilter(m_pFactory, CLSID_HttpContentFilterPP, L"application/x-javascript");
   hr = m_pSession->RegisterMimeFilter(m_pFactory, CLSID_HttpContentFilterPP, L"text/javascript");
  }
 }


        当用户访问某个网页的时候,最先到达MIME类型是text/html,这样就会引发PP构造函数,从而实现了JS的MIME类型注册,这样后面的JS就会被拦截。

 

3. 在Start函数中实现类型判别:

 if (!wcscmp(szUrl, CONTENT_TYPE_TEXT_JS))
 {
  m_js_type = TEXT_JS;
 } 
 else if (!wcscmp(szUrl, CONTENT_TYPE_JS))
 {
  m_js_type = JS;
 }
 else if (!wcscmp(szUrl, CONTENT_TYPE_X_JS))
 {
  m_js_type = X_JS;
 }

m_js_type是我定义的一个枚举成员变量。

 

4. 在过滤处理函数中实现过滤:

if(m_js_type != NONE)
 {

//过滤处理

//...

CString S = pbuff;
  _ASSERTE(m_pHTMLBuffer);
  int c = 0;
  /*while(c!=g_KeyWordsOrgin.GetCount())
  {
   CString s = g_KeyWordsOrgin[c];
   S.Replace(s, g_KeyWordsReplace[c]);
   c++; 
  }*/

  CString s_or("aaaaaaaaa;");
  CString s_or2("bbbbbbbbb;");

  int len = S.GetLength();

  if (S.Find(s_or)!=-1)
  {
   CString s_re("aaaaaaaaa; alert('a');");
   if(S.Replace(s_or, s_re))
   {
    ATLTRACE(_T("aaaaaaaaa replaced success!"));
   }
  }
  else if(S.Find(s_or2)!=-1)
  {
   CString s_re2("bbbbbbbbb; alert('b');");
   if(S.Replace(s_or2, s_re2))
   {
    ATLTRACE(_T("bbbbbbbbb replaced success!"));
   }
  }
  
  memcpy(pbuff, S.GetBuffer(0), S.GetLength());
  pbuff[S.GetLength()] = 0;

  S.ReleaseBuffer();

//...

 }

 

四. 实验结果

        以某某站点为例,测试结果如下:



五. 总结

          上面的步骤是在微软提供的例子的基础上修改的,经过测试,成功地拦截到了JS脚本,并实现了JS脚本内容的修改。在实验中发现,IE下载到的JS脚本文件并没有修改掉,只是读取到内存中的脚本内容发生了变化,读者可查看IE缓存文件夹的响应脚本文件验证下。还有个问题就是同一个网页中,某些JS脚本拦截不到,过滤器的功能不够稳定,具体的原因可能是由于某些JS没有content-type标签,导致IE在探查JS类型的时候误判。具体可参考http://msdn.microsoft.com/en-us/library/ms775147(v=vs.85).aspx中关于MIME类型的探查算法。文章中若有错误恳请读者不吝文墨,批评斧正。谢谢!

       下面是我搜到的关于MEMIFILTER的一些站点:

       http://msdn.microsoft.com/en-us/library/aa767916(VS.85).aspx

       http://msdn.microsoft.com/en-us/library/ms775148(v=vs.85).aspx

       http://msdn.microsoft.com/en-us/library/ms775147(v=vs.85).aspx

       http://support.microsoft.com/kb/260840

       http://blog.csdn.net/lion_wing/article/details/839134

       http://www.pudn.com/downloads74/sourcecode/windows/internet/detail271630.html

       http://www.vckbase.com/index.php/wv/1145
       http://www.qingfengju.com/article.asp?id=68

 

源代码下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值