美文网首页爬虫
PyV8、PyExecJS、js2py区别

PyV8、PyExecJS、js2py区别

作者: 叫我老村长 | 来源:发表于2019-01-30 13:31 被阅读189次

    PyV8、PyExecJS---->模拟js执行

    js2py区别 ------> 装换代码本身,改为python可执行的,本质完全不同

    目前发现PyV8、PyExecJS比较靠谱,MacOS 安装PyV8的话用https://github.com/emmetio/pyv8-binaries,pip安装有问题。

    js2py虽然很方便,但是有时候代码会报错。

    PyV8例子

    import PyV8
    
    ctxt = PyV8.JSContext()
    ctxt.enter()
    test=ctxt.eval("""
    (function(p){
        function abc(t) {
            return "hello,"+t;
        }
        return abc(p);
    })
    """)
    if __name__ == "__main__":
        print(test('world'))
    输出
    
    hello,world
    

    但是传python字典给js的函数,似乎没有用。不过可以json.dumps(dict) 传给js函数,js函数再JSON.parse(json_str)。

    pyv8自带的这种方法很奇怪,有的函数运行会得到和真正js不一样的结果,但是PyExecJS走PyV8就没有这种问题,真是奇怪。

    pyexecjs速度慢与pyv8的安装

    1. 1. 安装pyv8
    2. 2. pyexecjs修复
    3. 3. Js2Py

    使用PyExecjs写的代码,放在服务器上每次执行都需要好几秒钟才能返回,经过排查发现PyExecjs的锅,官方也有如下声明:

    One of cons of PyExecJS is performance. PyExecJS communicate JavaScript runtime by text and it is slow. The other cons is that it does not fully support runtime specific features.
    PyV8 might be better choice for some use case.

    原来PyExecjs是通过将要运行的js代码写到/tmp下面的随机文件里,然后调用相关的解释器执行该文件。没办法只能改用PyV8。
    使用pip install PyV8安装怎么装都不成功,老提示找不到<v8.h>,最后发现此github上有编译好的相关文件。

    安装pyv8

    安装步骤:

    1. 下载相应系统的压缩文件
    2. 解压得到PyV8.py与_PyV8.so(如果so文件不是这个名字要将它改成这样)
    3. 打开python解释器,查看sys.path找到site-packages目录位置(我这里是/usr/local/lib/python3.5/site-packages)
    4. 将上面两个文件复制到site-packages目录就可以了。

    pyexecjs修复

    安装pyv8后,pyexecjs默认会使用pyv8运行时。pyexecjs支持的运行时及优先级如下

    First-class support (runtime class is provided and tested)

    • PyV8 - A python wrapper for Google V8 engine,
    • Node.js
    • PhantomJS
    • Nashorn - Included with Oracle Java 8

    Second-class support (runtime class is privided but not tested)

    • Apple JavaScriptCore - Included with Mac OS X
    • Microsoft Windows Script Host (JScript)
    • SlimerJS
    • Mozilla SpiderMonkey

    相同的代码部署在服务器上运算慢的原因估计就是开发环境使用的是Apple JSCore运行时,部署在服务器上只有Nashorn。
    安装完pyv8后使用pyexecjs会报如下错误:

    AttributeError: module ‘contextlib’ has no attribute ‘nested’

    这是由于在python3中,contextlib的nested函数被去掉了,因为with语法可以达到相同的作用,要想让pyexecjs正常运行,需要修改pyexecjs下的_pyv8runtime.py。将第51行代码
    with contextlib.nested(PyV8.JSContext(), PyV8.JSEngine()) as (ctxt, engine):
    改为
    with PyV8.JSContext() as ctxt, PyV8.JSEngine() as engine:

    Js2Py

    mac os下代码可以正常运行,但部署到centos上PyV8老崩溃,提示segmentation fault。努力了两天也不行,最终找到这个js2py,它可以将js代码转成python代码,也内置了一个js运行环境。笔者将js转换成python代码后成功运行。

    python-js2py模块的应用

    分享一个破解js方法的模块吧。
    在工作中有些网站是涉及到加密的问题,因为作者所做的网站大部分涉及到登录问题,那么在登录的时候我们知道最有可能遇到的是账号密码的加密,今天就分享一下自己在工作中实际遇到的问题以及解决方法,直接上代码如下:

    {"LoginKey":"46fd82c2-3108-b2a4-3056-0eaa426f975b","data":"{\"User_Oid\":\"账号\",\"User_Password\":\"密码\"}"}
    
    

    自己想要模拟登录的时候发现无缘无故的多了一个键值对:"LoginKey":"46fd82c2-3108-b2a4-3056-0eaa426f975b",此时不禁在想难道是个固定值,又或者在其他页面上生成的数据吗?然而当作者实际测试的时候发现固定值这个想法太简单了,可是也没有从其他的页面获取到值呀?不禁猜想一定是js文件进行了加密,于是那就找js文件吧,查找js文件这里作者就不教大家怎样查找了,如果不会可以去作者之前的分享看一下,因为涉及到工作网站,这里作者也不对外提供网址了,今天分享的是我们找到js文件是怎样才能破解和实现。这里作者贴出来找到的js函数:

    function s4() {
    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
    };
    function guid() {
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }
    
    

    这就是生成LoginKey值的js文件,那么进行破解吧,js2py模块的应用正式开始。

    1. 安装
    pip install js2py
    
    
    1. 应用
    #coding=utf-8
    
    import js2py
    
    LoginKey = js2py.eval_js('''
    function s4() {
    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
    };
    function guid() {
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }
    var a = guid()  // 在这里是需要调用的
    ''')
    print LoginKey 
    
    

    好了是不是破解到这里就结束了,是不是这个模块的应用是非常简单的,破解js说明白了就是怎样找到加密的函数才是关键。

    js的逆向解析

    过程:

      知道如何寻找登录的接口
    
      知道如何确定js的位置
    
      知道如何观察js的执行过程
    
      知道js的执行方法
    

    1. 确定网站的登录的接口

    1. 登录的form表单中action对应的url地址
    2. 通过抓包可以发现,在这个url地址和请求体中均有参数,切换到手机版,参数的个数少,分析js

    2. 确定js的位置

    1. 通过点击按钮,然后点击Event Listener,部分网站可以找到绑定的事件,对应的,只需要点击即可跳转到js的位置
    2. 部分网站的按钮可能并没有绑定js事件监听,那么这个时候可以通过搜索请求中的关键字来找到js的位置,比如livecell

    3. 观察js的执行过程

    1. 找到js的位置之后,我们可以来通过观察js的位置,找到js具体在如何执行,后续我们可以通过python程序来模拟js的执行,或者是使用类似js2py直接把js代码转化为python程序去执行
    2. 观察js的执行过程最简单的方式是添加断点
    3. 添加断点之后继续点击登录,每次程序在断点位置都会停止,通过如果该行有变量产生,都会把变量的结果展示在Scoope中

    4. 执行js

    观察代码知晓需要哪些参数,比如:

    1. 我们要登录需要对密码进行加密和获取rkey字段的值
    2. rkey字段的值我们直接发送请求rkey请求就可以获得
    3. 密码是先反转然后使用RSA进行加密, js代码很复杂, 我们希望能通过在python中执行js来实现

    实现思路:

    1. 使用session发送rKey获取登录需要信息
    2. 根据获取信息对密码进行加密
    3. 使用session发送登录请求
    import requests
    import json
    import js2py
    
    # - 实现思路:
    #   - 使用session发送rKey获取登录需要信息
    #     - url: http://activity.renren.com/livecell/rKey
    #     - 方法: get
    #  获取session对象
    session = requests.session()
    headers = {
        "User-Agent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Mobile Safari/537.36",
        "X-Requested-With": "XMLHttpRequest",
        "Content-Type":"application/x-www-form-urlencoded"
    }
    # 设置session的请求头信息
    session.headers = headers
    
    response = session.get("http://activity.renren.com/livecell/rKey")
    # print(response.content.decode())
    n = json.loads(response.content)['data']
    
    #   - 根据获取信息对密码进行加密
    #     - 准备用户名和密码
    phoneNum = "131..."
    password = "****"
    #     - 使用js2py生成js的执行环境:context
    context = js2py.EvalJs()
    #     - 拷贝使用到js文件的内容到本项目中
    #     - 读取js文件的内容,使用context来执行它们
    with open("BigInt.js", 'r', encoding='utf8') as f:
        context.execute(f.read())
    
    with open("RSA.js", 'r', encoding='utf8') as f:
        context.execute(f.read())
    with open("Barrett.js", 'r', encoding='utf8') as f:
        context.execute(f.read())
    
    # - 向context环境中添加需要数据
    context.t = {'password': password}
    context.n = n
    #     - 执行加密密码的js字符
    js = '''
           t.password = t.password.split("").reverse().join(""),
           setMaxDigits(130);
           var o = new RSAKeyPair(n.e,"",n.n)
            , r = encryptedString(o, t.password);
          '''
    context.execute(js)
    # - 通过context获取加密后密码信息
    # print(context.r)
    password = context.r
    #   - 使用session发送登录请求
    #     - URL: http://activity.renren.com/livecell/ajax/clog
    #     - 请求方法: POST
    #     - 数据:
    #       - phoneNum: 15565280933
    #       - password: (加密后生产的)
    #       - c1: 0
    #       - rKey: rkey请求获取的
    data = {
        'phoneNum': '131....',
        'password': password,
        'c1':0,
        'rKey':n['rkey']
    }
    
    # print(session.headers)
    response = session.post("http://activity.renren.com/livecell/ajax/clog", data=data)
    print(response.content.decode())
    
    # 访问登录的资源
    response = session.get("http://activity.renren.com/home#profile")
    print(response.content.decode())
    

    相关文章

      网友评论

        本文标题:PyV8、PyExecJS、js2py区别

        本文链接:https://www.haomeiwen.com/subject/qzvbfqtx.html