html调用js函数_python爬虫之简单入门级js逆向

特别说明:本文针对逆向小白,大神请移步。

简单解释js逆向:当我们抓取网页端数据时,如果请求参数是固定不变或者是上个请求返回的,那么我们用可以python直接模拟这个请求得到要爬取的数据。

但是如果发现有些请求参数每次都会变化,而且在其他请求中也找不到这个参数,那么这个参数是哪里来的?要怎么才能得到这个参数?

答案就是这些加密参数是由JavaScript(简称JS)生成的。

所以想到得到这个参数,就需要我们快速定位JS的位置,并观察JS的执行过程,知道JS的执行方法。这个过程就叫JS逆向。

知道了参数是怎么来的,我们就能用各种手段去生成了,这个后面讲。

当然js逆向不止这么简单,还有些网站不单请求参数加密,请求返回的数据也加密了。想要了解更多,请移步本公众号合作平台:百度。网址https://www.baidu.com/

276945be536eb698c87c546ea72a1685.png

案例:爬虫练习网站 http://glidedsky.com 第10关。

所需环境:谷歌浏览器

0e76057b8aab4cdccbc9abf04425ef23.png

点击待爬取网站,f12打开开发者工具。清空当前产生的各种请求,然后点击下一页,找到目标请求:

c38c981cb0ddedbb1f5f56ea9cc1352c.png

90c593313c89fbf7e8b8825476ac05e2.png

看下第一页请求:

2e21ca6d51d4d802d0522986cb1389bc.png

第二页的请求:

6c8c8f569315718d9c14354ffe3272bd.png

这里要解决的只有两个:

  • 参数 t

  • 请求中的sign

以后看到t这样的参数就不用浪费太多时间去分析了,就是一个时间戳,秒为单位是10位数,毫秒位单位是13位数。

用python生成当前时间戳代码:10位:int(time.time())。13位:int(time.time()*1000)

接下来定位参数加解密位置。

定位加密位置

选择Search打开全局搜索

1cc77deb75db2022ca6c16a466496519.png

输入sign,回车

540da127e5587b89ed2a3af9cead5d0c.png

发现有四个文件里包含字符串"sign",分别点进去,ctrl+f在当前文件内搜索sign

d34f7ebb25114e75f73347ab0afad450.png

然鹅!四个文件都搜一遍,发现都没有sign,只有assign对不对?这就说明这个参数变量名被隐藏起来了,那咋整?别慌,有第二种方法定位

276945be536eb698c87c546ea72a1685.png

控制台切换到Sources,选择XHR/fetch Breakpoints,在点击+

e5643aaa20630153629e8a55ca6eb7ed.png

前面我们知道我们要抓取的url是这个:http://glidedsky.com/api/level/web/crawler-javascript-obfuscation-1/items?page=1&t=1595651857&sign=9820aae679871cf4d4524b0bc8934944863d3234

?前面的“" http://glidedsky.com/api/level/web/crawler-javascript-obfuscation-1/items"是接口,我们这里输入这个接口地址,可以全部输入,也可以只输入后面的部分。我只输入 crawler-javascript-obfuscation-1/items

19bdf240746658054cd63e4910e5fa92.png

这个的作用是啥呢?

就是你可以针对某一个请求或者请求的关键字设置断点,这里的关键字就是crawler-javascript-obfuscation-1/items,断点就是程序运行到断点处停住,可以实时查看各变量和值和函数的入参。

解释完,我们网页上直接刷新或者点击下一页,看控制台能不能断下来。

98c986424cfe246eabda44ab0669627f.png

这样就已经断下来了,如果你的没有断下来,请重复以上过程,或者重启浏览器,重启电脑,重启路由器。。。。

断在这里意思是这个请求还没发到后端服务器,被暂停了。

然后,我们看这里

3d64894d57c2918897e2d93c1be8d2c5.png

主要解释这5个按钮的作用,可以点击或者用快捷键,鼠标移到按钮上面会显示快捷键。

第一个:跳到下一个断点处,这里我们没有设置其他断点,所以点这个页面会恢复执行。

第二个:执行下一行代码。

第三个:进入函数。

第四个:跳出函数。

第五个:网上追一层调用栈

这个案例我们不需要打其他断点,只要用到的是调用栈的知识。解释一下什么叫调用栈。举个例子:我用python定义一个a函数,一个b函数,程序执行过程是先执行a函数,再执行b函数。最后得出结果。这时这个程序的调用栈就是:

b();

a();

这里第一行的b函数代表是最后执行的,也就是调用栈的顺序跟程序的执行顺序相反的,最后一行的代表的是第一个被调用的函数。

然后我们回到浏览器,看Call Stack,这里就是js到断点处的调用栈,我标了数字表示顺序:

f04f84594431f41bbf916008ca5a553f.png

因为js到断点处是准备发请求了,那么请求参数肯定是已经生成了的。没有参数它怎么能发请求呢,对吧。

所以我们要往上追调用栈,看看是哪个地方生成sign参数的。从上往下依次点进去看看。

然后到第四个,诶,这不就是sign生成的地方嘛!

293b8f3f35844154e7190a34477c3bbe.png

分析加密

找到加密地方了,接着是分析加密。看起来很简单,sign就是sha1()函数得到的,传入的实参是'Xr0Z-javascript-obfuscation-1' + t。

Xr0Z-javascript-obfuscation-1是字符串常量。t是第三行来的,t = Math.floor(($('main .container').attr('t') - 99) / 99)。

前面说了,t是时间戳,但是这里看好像还做了其他的处理,为了避免翻车,我们再分析一波。

$('main .container').attr('t')这个意思是从html页面container节点获取属性为t的值,我们全局搜索container:

55e891d7b2bcfd072dacbd4cc4ca127e.png

点进去:

6758526b4f610ac123a569f358f20be2.png

9fb124ec5d38b04b633e1ee00481a7e3.png

3a0f2d9c91f33fa6a7cefba88ff8f8d6.png

看来不止是当前时间戳这么简单,点击下一页先请求这个html页面

68c29cddf210b94da49e2700f7088c8e.png

然后从这个html中获取t,再传给这个就是t = Math.floor("157970230407"- 99) / 99)。

js是个很骚的语言,"157970230407"- 99这里字符串-整数,换python是会报错的,但是js的-号是只要有其中一个对象为整数,那么就会把其中的字符串对象转换成整数再做减法。

+号是只要其中一个对象为字符串,就是拼接字符串。

Math.floor不懂就是百度搜索 js Math.floor。可以知道是把浮点数转换成整和python里的int()函数效果一样。

现在我们知道了t的生成过程:

1、先用python请求http://glidedsky.com/level/web/crawler-javascript-obfuscation-1?page=4 得到html源码。

2、用正则或者bs4从html中提取出$('main .container').attr('t')的值,再减去99然后除以99得到一个浮点数,接着用int转换成整数。

继续分析sign。

上面分析了sign就是sha1()函数得到的,传入的实参是'Xr0Z-javascript-obfuscation-1' + t,这里+号是表示拼接字符串。

用鼠标选中这些内容

daf5d95c46369cfb661d5cdb9361a137.png

然后鼠标放上去,可以看到函数运行的结果:

dc01d7012b35da5533bd2e1d2010e30d.png

同样方式选中sha1括号里面的内容,查看传入的参数:

7660159b2785152bc9b5f5bc65262e76.png

可以看到函数实参是"Xr0Z-javascript-obfuscation-11595658892"。

到这里我们一般会有两种处理方式:

第一种是把这个sha1函数从网页上抠下来,保存为js文件,这样我只要用python的execjs库或者用nodejs调用这个js函数,把实参传进去,就能生成我们要的结果。这也是js逆向最常用的一种方式。

第二种方式是用python还原算法,直接用python的加密库生成所要参数。因为一般反爬做的加密,都是用成熟的加密算法,都是公开的算法,都是经过时间考验的比如base64,md5,sha1,AES,RSA等等,很少有人用自己写的加密算法。

这些算法每个语言都可以实现,只是语法不同。所以我们只要知道js加密用的什么加密算法,加密的具体逻辑。就能用python的加密库区生成参数。

这个案例我们用第二种,以后只要看到base64,md5,sha1,第一反应都可以不用去扣代码,像现在这个案例,函数名为sha1。第一步:先猜测他就是sha1算法。

第二步:

验证猜想,借助在线加密网站,百度一下 在线sha1加密http://www.ttmd5.com/hash.php?type=5

b12c5135f16fa9af949bcfd11641cbda.png

把这个参数复制下来,粘贴到网站上,得到结果:

4f8eaa4f0ef9d23624efa6838db107d4.png

然后对比浏览器执行结果:

0f55682b0d13298f7f22b7cf56b7d46d.png

是不是一样?

第三步:

猜想正确,夸自己牛逼。

8233e630e5e8989b52111dd208df3b9d.png

第四步:

写Python代码

import requests

import hashlib

from bs4 import BeautifulSoup

def get_str_sha1(str):

sha1 = hashlib.sha1(str.encode('utf-8'))
encrypts = sha1.hexdigest()
print(encrypts)
return encrypts

def get_t(page):

params = {
    'page': page,
}
response = requests.get('http://glidedsky.com/level/web/crawler-javascript-obfuscation-1', headers=headers,
                        params=params,verify=False)
soup = BeautifulSoup(response.text,'html.parser')
t_str = soup.find(class_="py-4").find(class_='container')['t']

t = int((int(t_str)- 99) / 99)
return t

def req(page,t,sign):

params ={
    'page': page,
    't': str(t),
    'sign': sign,
}
response = requests.get('http://glidedsky.com/api/level/web/crawler-javascript-obfuscation-1/items',
                        headers=headers, params=params,verify=False)
return response.json()

def main():

for i in range(100):
    page = str(i+1)
    t = get_t(page)
    sign = get_str_sha1('Xr0Z-javascript-obfuscation-1{}'.format(t))
    json_text = req(page,t,sign)
    print(json_text)

headers = {
   'Connection': 'keep-alive',
   'Accept': '/',
   'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
   'X-Requested-With': 'XMLHttpRequest',
   'Referer': 'http://glidedsky.com/level/web/crawler-javascript-obfuscation-1?page=3',
   'Accept-Language': 'zh-CN,zh;q=0.9',
   'Cookie': '可以模拟登录获取cookies没有任何加密,也可以从浏览器复制下来'
}
main()

全文完。

一看时间还早,那么把第一种扣代码的方式也大概讲一下。

前面一样到这里,鼠标左键点一下,下一个断点

247d0785c2859b0cd48d6dcc6cabbe55.png

然后刷新页面,会断在这个函数:

da9219067bf6fdff7231d20545478b3c.png

然后我们进入函数,才能知道他那个函数的代码放在哪里,知道在哪里才能扣下来。前面说了进入函数,按这个

6fbb842f5ee50a8cc1b60ded506baf2b.png

来到这里:

991af6b2d59b48c25663e1a9150b578a.png

可以看到,new t(!0).update(s)[h]()就是最终结果,h是常量

774c3b1fcd125cea7a46132851b6e216.png

我们把这行代码复制下来,放到js调试助手(公众号发送"调试助手"可获得下载链接)

5320808bf2dd34ad651bc2f199f45725.png

定义一个get_sign函数,

42ce82890a0dd79a702c91621fc6e47e.png

然后加载代码

86c638e8bd4ebcd880f3034443e19c7d.png

选择函数传入参数调用运行:

66fda2962cdf53d8934fcaae401f4a4d.png

提示t没有定义

129ee1ea2134efc1c76d0813c0de8faa.png

那么我们去网页上找这个t,鼠标移动到t上面,点击跟进去

654221ec0509a7a90c6b5bc790943529.png

函数t就是在当前文件的最上面

7501e6b1f5c89a2aa4aeaadf95358e53.png

看看最后函数的返回值:

810e8f7e024113032469a1abef689ba8.png

所以我们把这里把整个t函数复制下来,11行-182行(不懂js自己去看基础语法,能看懂代码就行)复制到get_sign函数上面,加载,运行。

1a2b2451ecedc5418ce5cc57bf000228.png

得到结果,和浏览器对比,是一样的

be36776d8b0e38a13702e162fae1c1e7.png

接着我们要把这个保存为一个js文件,供python调用:

760e3a38ddb8d941e1f1226bf970ae93.png

f45296f3984eaf0da0f45fd3df2e109d.png

所用到第三方库是exexjs。

安装方式:pip install pyexecjs

然后写代码:

import execjs

def get_sign(s):

with open(r'C:\Users\hp\Desktop\js.js', "r",encoding='utf-8') as f:
    ctx = execjs.compile(f.read())
    sign=ctx.call('get_sign',s)
print(sign)
return sign

sign=get_sign("Xr0Z-javascript-obfuscation-11595665680")

运行结果:

276c085423fffbbff2c4502c6312ad05.png

大功告成!告辞。

5fbec1d5e7c2ac4197a034216ac91942.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值