js 字符串减去字符串_python爬虫之简单入门级js逆向

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

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

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

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

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

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

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

37376b06a3cbf2abc03df7c9f1b45291.png

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

所需环境:谷歌浏览器

4a91c6fdeb6d27a32454177df4879e0f.png

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

be6fdc00c801b8ff3bc371343e3a36bb.png

7594a5af5941e2eb13fd1cc964b8f27a.png

看下第一页请求:

f46b18a0f0eec41e5523fff55e796bdf.png

第二页的请求:

5b62632e29464f2d53b018fc8096c6b9.png

这里要解决的只有两个:

  • 参数 t

  • 请求中的sign

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

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

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

定位加密位置

选择Search打开全局搜索

5b8eadfd70ac2d9d2d0bd6677681f6ee.png

输入sign,回车

9d7c838b09d1821ae8daae62944b7a26.png

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

c370f24d21af8560db84888079dded99.png

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

37376b06a3cbf2abc03df7c9f1b45291.png

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

7420a9ae75932d1a64e60ec044d345d5.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

fac121d9f6c19a8f55c1e975dd6c5d65.png

这个的作用是啥呢?

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

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

28471ca6d725077ddd6017d2ad87fa7c.png

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

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

然后,我们看这里

a28d13a38daf28483ac24ab83b1bac13.png

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

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

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

第三个:进入函数。

第四个:跳出函数。

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

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

b();

a();

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

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

a52570f26f610381691ae82cc487837c.png

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

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

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

ad5357fb9af8b836d7608a28baebab1a.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:

bae6500770759530d785a0cc0824102d.png

点进去:

f0539ca48a9bf0f3671da9cf3830fa05.png

95cb3e4e5be2abfe7231f7a51f668f8d.png

888d5ebd7d1669110f90692dd62edddd.png

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

779cd8d41134b11fbcd52d3a1c28aeee.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,这里+号是表示拼接字符串。

用鼠标选中这些内容

e9a3de81cd69b130c84fb0338538eaab.png

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

a59b063e130a68f3dbee2cf031d63709.png

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

c3f6129b3c37355786d64f163f021f73.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

e611bf8b0fc031628ca2693fc6f12b3e.png

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

27226cd5559620b59c565459690c4a11.png

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

4346e25e7469b27c1e5d6398f28894e6.png

是不是一样?

第三步:

猜想正确,夸自己牛逼。

38b5a4a5654ebe6ec040d825d2e10f33.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()

全文完。

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

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

d8d10a5c4b19f201271ef89f3fa239e0.png

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

9911f0489ef6b350d1f14b9adfd08a87.png

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

5785e0c8686b21377c783caa1a7c5f07.png

来到这里:

a556484e2ce734c766c7f4b4c53d1e83.png

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

f2ffa3360f2f25f1d3a045faa0178009.png

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

37b8ffe71c8a359a06fe4122bfc8c2c9.png

定义一个get_sign函数,

2d78f2aae39a4e7c354251754d0103f1.png

然后加载代码

84f7ac5be7676f4e29405c0ebdd8598b.png

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

00923f76dc72b33ac1168a328e7a6bbf.png

提示t没有定义

9dcc85f80c0f818a1b6d514a81cd4106.png

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

7a30c354584c19cb047b41c25e340814.png

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

972124a79f84dd3ec5c2c3ba13628ea1.png

看看最后函数的返回值:

d7347e56b03192f0563eba069b70e144.png

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

6f49decaf4f04bf4495572d5738f0a7e.png

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

3dd7115bf2e69ac9730d5f53b73272a5.png

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

9d97ed3e7b774d54b7685c98d12ef98c.png

c84d8c04bc7dfcc521883bee0cdaf26f.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")

运行结果:

1c7d89b0cf67e5b353cd1e09f67a76ae.png

大功告成!告辞。

bc90b6a4b469a681921d3acd70b5dd90.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值