Winform下动态执行JavaScript脚本获取运行结果,谈谈网站的自动登录及资料获取操作

长平狐 发布于 2012/06/11 17:29
阅读 532
收藏 0

为了有效阻止恶意用户的攻击,一般登录都会采用验证码方式方式处理登录,类似QQ的很多产品的验证码处理,但在一些OA系统中,系统通过非对称加密方式来处理登录的密码信息,登录页面每次提供对密码进行加密的公钥是不同的,因此如果要模拟登录,就需要先获取公钥,然后根据公钥把输入的密码加密,然后通过POST提交给服务器进行验证登录。由于公钥是页面刷新变化的,而加密是通过Javascript脚本进行加密,如下面的登录页面源码所示。

 

< meta  http-equiv ="Content-Type"  content ="text/html; charset=gb2312" >  
< link  rel ="stylesheet"  type ="text/css"  href ="/templates/2008/index.css" >  
< link  rel ="shortcut icon"  href ="/images/tongda.ico" >  
< script  src ="/inc/js/rsa/jsbn.js" ></ script >< script  src ="/inc/js/rsa/prng4.js" ></ script >< script  src ="/inc/js/rsa/rng.js" ></ script >< script  src ="/inc/js/rsa/rsa.js" ></ script >  
< script  type ="text/javascript" >  
function  CheckForm()
{
   
var  rsa  =   new  RSAKey();
   rsa.setPublic(
" 97e256ec6147b7aadc46a353b5c5d707a895b402d114290c0c24a28919507569 " " 10001 " );
   
try {
      document.form1.PASSWORD.value 
=  rsa.encrypt(document.form1.PASSWORD.value);
   }
   
catch (ex){
      
return   false ;
   }
   
return   true ;
}
</ script >  
</ head >  
 
< body  onload ="javascript:document.form1.PASSWORD.focus();" >  
< br >  
< br >  
< br >  
< br >  
 
< div  align ="center" >  
< form  name ="form1"  method ="post"  action ="logincheck.php"  autocomplete ="off"  onsubmit ="return CheckForm();" >  
 
  
< table  cellspacing ="0"  cellpadding ="0"  align ="center" >  
    
< tr  class ="img_field" >  
      
< td  align ="center" >< img  src ="/attachment/2090997160/index_1.jpg"  width ="651"  height ="241" ></ td >  
    
</ tr >  
    
< tr  height ="37"  class ="login_field" >  
      
< td  align ="center" >  
        
< b > 用户名 </ b >   < input  type ="text"  class ="text"  name ="UNAME"  size ="15"  onmouseover ="this.focus()"  onfocus ="this.select()"  value ="" >  
        
< b > 密码 </ b >   < input  type ="password"  class ="text"  name ="PASSWORD"  onmouseover ="this.focus()"  onfocus ="this.select()"  size ="15"  value ="" >  
< select  name ="UI" >  
            
< option  value ="0" > 标准界面 </ option ></ select >  
         
&nbsp; < input  type ="submit"  name ="submit"  class ="submit"  value ="登 录" >  
      
</ td >  
    
</ tr >  
  
</ table >  
  
< br >  
  
</ form >  

 

为了模拟登录,我们需要先获取页面的公钥信息,然后通过在C#中运行Javascript脚本,传递公钥和明文密码,然后获取脚本加密的结果,再提交给服务器处理。

如下面代码所示,获取公钥就是分析HTML源码,通过正则表达式匹配即可获取到,相对比较简单,但是要获取Javascript脚本的运行结果,就需要花点功夫了。

 

                     /// inc/js/rsa/jsbn.js /inc/js/rsa/prng4.js /inc/js/rsa/rng.js /inc/js/rsa/rsa.js
                     string  scriptUrl  =   " http://www.abc.cn:8080/inc/js/rsa/jsbn.js " ;
                    
string  scriptUrl2  =   " http://www.abc.cn:8080/inc/js/rsa/prng4.js " ;
                    
string  scriptUrl3  =   " http://www.abc.cn:8080/inc/js/rsa/rng.js " ;
                    
string  scriptUrl4  =   " http://www.abc.cn:8080/inc/js/rsa/rsa.js " ;
                    
string  referen  =   " http://www.abc.cn:8080 " ;
                    HttpHelper helper 
=   new  HttpHelper();
                    helper.Encoding 
=  Encoding.Default;

                    
string  mainContent  =  helper.GetHtml(referen, cookie, referen);

                    
string  regex  =   " rsa.setPublic\\(\ " ( ?< publicKey > . *? )\ " ,\\s*\ " ( ?< encrypt > . *? )\ " \\); " ;
                    Regex re 
=   new  Regex(regex, RegexOptions.IgnoreCase  |  RegexOptions.Singleline  |  RegexOptions.IgnorePatternWhitespace);
                    Match mc 
=  re.Match(mainContent);
                    
if  (mc.Success)
                    {
                        
string  publicKey  =  mc.Groups[ " publicKey " ].Value;
                        
string  encrypt  =  mc.Groups[ " encrypt " ].Value;
                        
string  pass  =  config.AppConfigGet( " ContactPassword " );

                        
string  source  =   "" ; // "var appName = \"Microsoft Internet Explorer\"; " + Environment.NewLine;
                        source  +=  helper.GetHtml(scriptUrl);
                        source 
+=  helper.GetHtml(scriptUrl2);
                        source 
+=  helper.GetHtml(scriptUrl3);
                        source 
+=  helper.GetHtml(scriptUrl4);
                        
// source = source.Replace("navigator.", "");
                        source  =  getJS(source);

                        encryptPass 
=  scriptEngine.Eval( " getRSAKey() " ,
                                             source 
+
                                             
" \r\nfunction getRSAKey(){\r\nvar RSA = new RSAKey();\r\nRSA.setPublic(\ ""  +
                                             publicKey  +   " \ " ,\ ""   +  encrypt  +   " \ " );\r\nvar Res  =  RSA.encrypt( ' " + pass + " ' );\r\nreturn Res;\r\n} " ).ToString();


                    } 
                    
#endregion

 

 上面的运行Javascript脚本,需要先把用到的脚本全部下载,把内容组合起来,然后添加一个虚拟的函数,运行得到返回结果接口,虚拟的函数一定要写正确,否则出来很多错误,得不到结果。

 上面的代码有source = getJS(source);这一句,是为了避免脚本调用navigator.appName来处理浏览器类型和版本的判断,有两种方式可以跳过这个处理,一个增加一个appName的变量,如var appName =**这样,然后统一替换navigator. 的字符,使得脚本判别浏览器代码失效;二是通过正则表达式替换掉响应的判断代码即可

         private string getJS(string strJS)

        {
            
if  ( ! Regex.IsMatch(strJS,  @" if\(j_lm \&\& \(navigator.appName == ""Microsoft Internet Explorer""\)\) {.+?dbits = 28;.+?} " , RegexOptions.Singleline)  ||
                
! Regex.IsMatch(strJS,  @" if\(navigator.appName == ""Netscape"" && navigator.appVersion < ""5"" && window.crypto\) {.+?} " , RegexOptions.Singleline))
            {
                
return   string .Empty;
            }

            strJS 
=  Regex.Replace(strJS,
                                     
@" if\(j_lm \&\& \(navigator.appName == ""Microsoft Internet Explorer""\)\) {.+?dbits = 28;.+?} " ,
                                     
" BigInteger.prototype.am = am2;\r\ndbits = 30\r\n " , RegexOptions.Singleline);
            strJS 
=  Regex.Replace(strJS,
                                     
@" if\(navigator.appName == ""Netscape"" && navigator.appVersion < ""5"" && window.crypto\) {.+?} " ,
                                     
string .Empty, RegexOptions.Singleline);
            
return  strJS;
        }

 

得到处理过的密码密文 ,一般通过POST方式提交登录页面,即可完成系统的登录了,然后继续可以通过HttpRequest方式获取系统各种页面的信息了(如联系人等),如下面所示。

             string referen = "http://www.abc.cn:8080/";

             string  loginUrl  =   " http://www.abc.cn:8080/logincheck.php " ;
            
string  login  =   " test " ;
            
string  loginPostData  =   string .Format( " UNAME={1}&PASSWORD={0}&UI=0&submit={2} " , encryptPass, login,  " %B5%C7+%C2%BC " );

            
string  conctactUrl  =   " http://www.abc.cn:8080/general/ipanel/user/search.php " ;
            
string  itemRegex  =   " <tr\\s*class=\ " TableLine\\d\ " >\\s*(.*?)\\s*</tr> " ;
            
string  memberRegex  =   " <td.*?>\\s*(.*?)\\s*</td> " ;

            List
< ContactInfo >  contactList  =   new  List < ContactInfo > ();
            HttpHelper helper 
=   new  HttpHelper();
            helper.Encoding 
=  Encoding.Default;

            
string  result  =  helper.GetHtml(loginUrl, cookie, loginPostData,  true "" , loginUrl);

 最后程序处理登录后,自动获取联系人的界面效果如下所示:

 

 

 除了上面的操作方式,还有一种途径是通过WebBrowser控件实现数据的自动提交,WebBrowser控件处理脚本的运行更加方便,但缺点是这个控件相对较慢,首先我介绍一下这种方式,在按钮触发中调用控件的Navigate函数,打开相应的登录链接地址。

webBrowser1.Navigate( " http://www.abc.cn:8080 " );

 

接着在浏览器控件的页面完成函数处理中对数据进行处理,处理的思路就是调用脚本对输入的内容进行加密,然后再触发提交按钮即可完成页面的登录,记录登录的信息,然后再去获取相关的页面内容信息,不过这种控件处理相对没那么大的弹性处理,不过可以作为一些功能的补充使用。

         private   int  numtries  =   0 ;
        
private   void  webBrowser1_DocumentCompleted( object  sender, WebBrowserDocumentCompletedEventArgs e)
        {
            
if  (webBrowser1.Document.GetElementById( " UNAME " !=   null )
            {
                webBrowser1.Document.GetElementById(
" UNAME " ).SetAttribute( " value " " 陈建才 " );
                webBrowser1.Document.GetElementById(
" PASSWORD " ).SetAttribute( " value " " voowoo770916 " );

                
if  (numtries  <   2 )
                {
                    IHTMLWindow2 login 
=  (mshtml.IHTMLWindow2)webBrowser1.Document.Window.DomWindow;
                    
// login.execScript("document.forms[0].submit();", "javascript");
                    login.execScript( " CheckForm(); " " javascript " );
                    
string  value  =  webBrowser1.Document.GetElementById( " PASSWORD " ).GetAttribute( " value " );
                    encryptPass 
=  value;

                    GetContact();
                    numtries
++ ;
                }
            }
            
if  (webBrowser1.Document.GetElementById( " userName " ==   null )
            {
                numtries 
=   0 ;

                
// string cookieString = webBrowser1.Document.Cookie;

                
// CookieCollection cc = new CookieCollection();
                
// CookieManger.SetCKAppendToCC(cc, cookieString, " http://www.abc.cn :8080");
                
// cookie.Add(cc);

            }
        }

 通过浏览器接口,我们可以实现页面内容在不可以见的浏览器控件中呈现,然后获取相应的页面对象或者页面源码进行分析,可以得到更加丰富的数据,模拟浏览器的实际操作和获得真实的显示结果。

 

 

 


原文链接:http://www.cnblogs.com/wuhuacong/archive/2010/11/08/1871866.html
加载中
返回顶部
顶部