phantomjs爬虫小记

phantomjs爬虫小记

1.前言

爬虫能力是web 扫描器发现能力第一要素,如果爬虫能力一般会大大限制web扫描器的发现漏洞能力,因此写好爬虫是web扫描器的第一步。

tsrc上有一篇好文章写得爬虫的大家可以先看看:https://security.tencent.com/index.php/blog/msg/34  ,他们的爬虫是pyqt来写的,但是我自己感觉pyqt太重,所以我选择了phantomjs来做。    

http://phantomjs.org/这是他的官网,安装还有函数什么的说的太清楚了,大家自己慢慢看。

2.爬虫分析页面的五大功能

根据tsrc上的文章,我们知道:

1.javascript动态解析 

2.hook所有的网络请求  

3.静态页面链接分析 

4.自动分析表单 

5.自动交互是爬虫的5个大功能。

除了自动交互,其它四个功能phantomjs完成都是轻轻松松的。 下面简单说下如何轻松实现这四个功能:

1.javascript动态解析,phantomjs在打开这个url时候就会自动使用自己的webkit内核去执行javascript。

2.hook所有的网络请求,phantomjs有page.onResourceRequested方法来hook所有的网络请求,我们在这个函数里面获取url。

3.静态页面链接分析和自动分析表单。 phantomjs提供page.evaluate,这个方法会创建一个“沙盒”来解析javascript,这里面的变量和phantomjs脚本的变量是不一样的,两个环境就不同。我们可以使用这个沙盒执行javascript,以此获得静态页面链接分析和表单。

3.头疼的自动交互

自动交互是最头疼的,不过到目前为止我也能达到一个比较符合我自己要求的结果了。

看看tsrc大牛说的。。。好简单。不知道他是咋自动交互的。。。。

你猜我之前咋做的。。。没错,按钮全点一遍,然后再获取静态页面的链接和表单。 = =|,这样做有一个很严重错误,假设html是这样的:

<div id=abc>-</div>
<input type=button id=1 onclick="abc.innerHTML ='<a href=click_link.php'+'?id=2>click_link.php?id=2</a>'" value=""/>
</div>
<input type=button id=1 onclick="abc.innerHTML ='<a href=click_link2.php'+'?id=2>click_link2.php?id=2</a>'" value=""/>

知道了吧,要是你点了两个,那么你只能获取到click_link2.php?id=2,而click_link.php?id=2你是获取不到的。 可以知道全点按钮的结果就是有可能会导致覆盖前一个dom。

我的解决思路是这样的,定义两个数组,一个存放分析到的链接(urls=[]),一个存放页面中的自动交互事件(onevents=[]),再定义两个函数,一个获取页面全部链接( function getallurl()),一个获取页面全部自动交互事件 function getonevents())。

遍历onevents,每执行一个自动交互事件,再获取一次页面全部链接和自动交互事件(即使执行getallurl(), getonevents(),当然这两个函数都有做是否重复的检查)

   这里要注意一个非常重要的东西 ,每执行一个自动交互事件,再获取页面全部链接和自动交互事件之前,需要我们等待几秒!这是因为如果该交互事件为ajax的请求,它需要等得到服务器返回数据以后,才继续渲染页面。(http://testphp.vulnweb.com/AJAX/index.php 这个页面就是这种情况)

4.更头疼的javascirpt等待问题

现在,我们需要等待。但是javascirpt并没有sleep函数。。

然后测试时候看到又发现这个问题:如果使用for循环直接会卡死浏览器,如果使用setTimeout下面的程序会继续运行,所以有什么好的办法能够实现类似sleep的功能(https://segmentfault.com/q/1010000002498592)。

后来我才知道,根本没有这种方法,你只能老老实实用setTimeout来做,咋做用?答案是:递归!

function doloop(i) {
      getallurl();//获取页面全部链接
      getonevents();//获取页面全部自动交互事件
      if (onevents.length ==0) {
           return;
      }
      if (i == (onevents.length - 1)) {
 	//触发onevent事件的伪代码
          setTimeout(function () {
                getallurl();
                getonevents();
               }, 1000);
      }
      else {
 	//触发onevent事件的伪代码
i = i + 1; //1
          setTimeout(function () {
               doloop(i);
         	}, 1000);
   	  }
}

你想到是这样做了么?反正我是想了好久。。。

5.代码时间

正所谓。no code,you say a jb。。

var url, cookie, postdata, auth, post, timeout, system = require('system'), page = require("webpage").create();
var referer = "";
if (system.args.length !== 6) {
    console.log('Usage: crawls.js <url> <cookie> <auth> <post> <timeout>');
    phantom.exit();
} else {
    url = system.args[1];
    cookie = system.args[2];
    auth = system.args[3];
    post = system.args[4];
    timeout = system.args[5];
    page.settings.loadImages = false;
    page.settings.resourceTimeout = timeout ? timeout * 1000 : 5 * 1000;
    headers = {};
    headers['Cookies'] = cookie;
    headers['authorization'] = auth;
    page.customHeaders = headers;
    page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36';
    page.viewportSize = {
        width: 1024,
        height: 768
    };
    page.onInitialized = function () {
    };
    page.onLoadStarted = function () {
        // console.log("page.onLoadStarted");
    };
    page.onLoadFinished = function () {
        // console.log("page.onLoadFinished");
    };
    //hook url request
    page.onResourceRequested = function (request) {
        postdata = request.postData ? request.postData : "";
        for (var i = 0; i < request.headers.length; i++) {
            if (request.headers[i].name == "Referer") {
                referer = request.headers[i].value;
            }
        }
        requesturl = request.url.replace(/"/g, "\\\"");
        requestdata = postdata.replace(/"/g, "\\\"");
        requestreferer = referer.replace(/"/g, "\\\"");
        console.log("hook_url:{\"url\":\"" + request.url + "\",\"method\":\"" + request.method + "\",\"post\":\"" + requestdata + "\",\"Referer\":\"" + requestreferer + "\"}hook_url_end");
    };
    //hook alert
    page.onAlert = function (msg) {
    }
    //log msg in page.evaluate
    page.onConsoleMessage = function (msg) {
        console.log(msg);
    };
    var method = post ? "POST" : "GET"
    //window.open() will hook,window.open() create a new page, old page not change
    page.onPageCreated = function (newPage) {
        newPage.onResourceRequested = function (request) {
            postdata = request.postData ? request.postData : "";
            for (var i = 0; i < request.headers.length; i++) {
                if (request.headers[i].name == "Referer") {
                    referer = request.headers[i].value;
                }
            }
            requesturl = request.url.replace(/"/g, "\\\"");
            requestdata = postdata.replace(/"/g, "\\\"");
            requestreferer = referer.replace(/"/g, "\\\"");
            console.log("hook_url:{\"url\":\"" + request.url + "\",\"method\":\"" + request.method + "\",\"post\":\"" + requestdata + "\",\"Referer\":\"" + requestreferer + "\"}hook_url_end");
        };
        newPage.close();
    };
    page.open(url, {
            operation: method,
            data: post,
        },
        function (status) {
            if (status !== 'success') {
                console.log('Unable to access network');
            } else {
                page.evaluate(function () {
                    var onevents = [];
                    var onclickstrs = [];
                    var urls=[];
                    //get form
                    function GetForm() {
                        var f = document.forms;
                        for (var i = 0; i < f.length; i++) {
                            url = f[i].action;
                            var inputs = f[i].getElementsByTagName('input');
                            var requestdata = "";
                            //without submit
                            var len = inputs.length - 1;
                            for (var j = 0; j < len; j++) {
                                if (j < len - 1) {
                                    requestdata = requestdata + inputs[j].name + "=" + inputs[j].value + "&";
                                }
                                if (j == len - 1) {
                                    requestdata = requestdata + inputs[j].name + "=" + inputs[j].value;
                                }
                            }
                            res = "hook_url:{\"url\":\"" + url + "\",\"method\":\"post\"," + "\"post\":\"" + requestdata + "\",\"Referer\":\"" + window.location.href + "\"}hook_url_end";
                            if (urls.indexOf(res) < 0) {
                                urls.push(res);
                                console.log(res);
                            }
                        }
                    }
                    //get href by tag
                    function handle_tag(tag, src) {
                        var elements = document.getElementsByTagName(tag);
                        for (var i = 0; i < elements.length; i++) {
                            res = "hook_url:{\"url\":\"" + elements[i][src] + "\",\"method\":\"get\",\"post\":\"\"" + ",\"Referer\":\"" + window.location.href + "\"}hook_url_end";
                            if (urls.indexOf(res) < 0 && elements[i][src].indexOf("javascript:") < 0 && elements[i][src].indexOf("mailto:") < 0) {
                                urls.push(res);
                                console.log(res);
                            }
                        }
                    }
                    //get href
                    function getallurl() {
                        GetForm();
                        handle_tag('a', 'href');
                        handle_tag('link', 'href');
                        handle_tag('area', 'href');
                        handle_tag('img', 'src');
                        handle_tag('embed', 'src');
                        handle_tag('video', 'src');
                        handle_tag('audio', 'src');
                    }
                    //get onevent
                    function getonevents() {
                        var allElements = document.getElementsByTagName('*');
                        var len = allElements.length;// allElements will change,len will change
                        for (var i = 0; i < len; i++) {
                            //js_code
                            if (allElements[i].href) {
                                javascript_code = allElements[i].href.match("javascript:(.*)");
                                if (javascript_code) {
                                    if (onevents.indexOf(javascript_code[0]) < 0) {
                                        onevents.push(javascript_code[0]);
                                    }
                                }
                            }
                            //onclick onevent too much,now only click
                            if (typeof allElements[i].onclick === 'function') {
                                onclickstr = String(allElements[i].onclick);
                                if (onclickstrs.indexOf(onclickstr) < 0) {
                                    onevents.push(allElements[i]);
                                    onclickstrs.push(onclickstr);
                                }
                            }
                        }
                    }
                    function doloop(i) {
                        getallurl();
                        getonevents();
                        if (onevents.length ==0) {
                            return;
                        }
                        if (i == (onevents.length - 1)) {
                            if (typeof  onevents[i] === "string") {
                                eval(onevents[i]);
                            }
                            else {
                                onevents[i].click();
                            }
                            //over~
                            setTimeout(function () {
                                getallurl();
                                getonevents();
                            }, 1000);

                        }
                        else {
                            if (typeof  onevents[i] === "string") {
                                eval(onevents[i]);
                            }
                            else {
                                onevents[i].click();
                            }

                            i = i + 1; //1
                            setTimeout(function () {
                                doloop(i);
                            }, 1000);
                        }
                    }
                    function main() {
                        doloop(0);
                    }
                    main();
                });
            };
            window.setTimeout(
                function () {
                    phantom.exit();
                },
                10000 /* wait 10 seconds (10000ms) */
            );
        });
}

url相似去重复代码,大家还是自己写吧~

代码写出来没几行,但是我花了四五天。。。

6.效果

http://testphp.vulnweb.com(这个站太卡。。测试时候建议延长超时时间)

http://demo.aisec.cn/demo/aisec/

7.问题

再说说的这个爬虫的问题。。。。

1.phantomjs自身可能crash的问题,我是不断更新phantomjs最新版,phantomjs现在已经是2.1.1,相信他会越做越好。

2.自动交互事件,我之获取了onclick事件和href的伪协议,还有好多on事件,我没写。。

3.这个onevents的数组是不是还有问题?假设我触发了一个交互事件,这个交互事件把前面的交换事件(allElements[i])修改了或者消除了,那怎么办?

4.等待超时时间设置为1秒,脚本运行超时时间设置10秒,仍可能因为网络问题,导致爬虫不准确,这个大家可以自己设置爬虫超时时间。

更多问题欢迎留言,或者直接联系我( wilson9x1@foxmail.com )

8.资料来源

https://security.tencent.com/index.php/blog/msg/34

http://phantomjs.org/

https://segmentfault.com/q/1010000002498592

http://epy.iteye.com/blog/1950135

本博客所有文章 如无特别注明 均为原创。 作者: wilson 复制或转载请 以超链接形式 注明转自wilson's blog 。

原文地址《

phantomjs爬虫小记

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章