Python爬虫 - js逆向之扣出某平台的_signature加密字段

前言

好久没有做逆向案例分析了,最近都在看同行朋友写好的案例,感觉学到很多,算是取长补短了

不多bb,机缘巧合下,拿到个目标网站

分析

首先抓个包,就看到请求参数里带着这几个参数

主要就是_signature了,其他的参数都不重要得想必你一看就知道啥意思了。

行,开始分析_signature了

先,全局搜一下,很好只有一个结果:

 点进去看到主要就这三个地方:

三个地方都打上断点:

继续滑动下拉,发现值已经有了:

鼠标放到变量上面,看到我们需要得_signature其实就是变量a了,而a由函数I(n,e)生成:

其中,n就是V.getUrl生成,e就是传进去的参数:

那取消刚才的断点,重新打上断点看看:

此时,可能你在调试的时候,有时候会出现滑块验证:

这个暂时不管,以后再系统的搞滑块哈

此时发现断上了:

跟进去,发现V.getUri进入到如下,感觉是在处理参数,不知道有没有我们要的sign

 怎么确定有没有,放开断点往下走,如果出现了那就是了,如果没有出现,那多半就不是了,如下:

 这个n的值明显没有sign,那就不是了

当然这个理论在这里行得通,但不绝对哈

好,接着看I,跟进去,发现如下,这里的e就是上面生成的n,t就是上面传进来的参数e,就是而且看到里面有sign相关的变量,90%是了

单步调试,一点一点跟着走看看:

找到关键点

发现上面都是些没有什么用的配置,就到了倒数第二行,貌似才有关键的东西,而且sign也在这一行里,先在这一行打个断点,然后继续走,一点放行,发现走到一个新的js里:

大概的看了一眼,应该是个加密算法,那是不是就是我们要的sign呢?不好说啊,直接点这个跳出去吧:

跳出去之后发现,变量o出现了一个很长的字段

 

拿着跟之前浏览器抓包看的_signature对比下长度,发现一致,那就是这个了,而实际的加密逻辑就是刚才进入的新的js文件alcrawler.js里

关键点找到了,那怎么把核心的加密逻辑扣出来呢?

我大概看了下,这里好像还不好单独把加密的逻辑抠出来,因为有很长的参数调用,而且我抠了下,不好扣,索性整个拿出来吧,结果发现整个也不多,就几百行,复制到本地吧

代码调试

放在本地,取名crawler,用node执行下看看:

发现报了个这个:

什么referrer,那我们搞爬虫的,再熟悉不过了,但是这个是在js里面啊,这就是涉及到补环境了,那么referrer属于哪个js对象里的呢,这个没法展开说了,我就直接说,referrer是document对象里的,js的全局对象有,window,doucument,navigator,global,location

行,这里补齐如下,地址给个主站的地址就行了:

继续执行,报错了:

但其实,懂js的朋友应该知道,其实window.document可以简写成document,这个就你们自己去研究为什么了,如下写,执行立马不报错了:

 当然也可以把window也补一下:

就不会报错了:

那现在我们要把那个生成sign的逻辑拿出来用下,怎么用呢?回到上面这个关键的有sign字段的那一步:

1

2

var o = (null === (n = window.byted_acrawler) || void 0 === n ? void 0 : null === (a = n.sign) || void 0 === a ? void 0 : a.call(n, i)) || "";

            

  

这个懂点js的都不陌生,我们拆开来看:

先看最外层,最外层的括号,如果括号里没有值,那就给o一个空字符串

 那根据上面的断点逻辑,肯定不会是空的,直接省略下,变成如下:

 先把var o 删了:

再通过||符号拆下:

 第一行,null肯定不会全等于(n=window.byted_acrawler)的,所以会走后面的逻辑,但是这一步,同时把window.byted_acrawler复制给了变量n

再看第二行,第二行是个三目运算,首先,0 ==== n肯定是不成立的,直接走后面的null=== (a = n.sgin),这个逻辑就跟第一行类似了,反正最后会把n.sign复制给变量a

再看第三行,这个跟第二行类似,同样的,会走到最后a.call(n,i)

不信可以看看这个下面:

先做简单的替换,本质的运算逻辑是没有区别的

 意思就是,那么长一句,最后会直接执行:window.byted_acrawler.sign.call(n, i)

那么,这两个参数,n,i是啥,先看看n:

再看看i,i就是个url的路径,没有带参数的那种

ok,i变量好说,这个n变量就有点不好搞了,这样,直接在控制台看下需要啥参数:

 这个报错就很有价值了,它只需要一个url就行了,根本不需要那个什么n变量,那就好说了

如下测试,发现不行,需要一个带有url属性的object对象 

 那行,整一个:

芜湖,出来了,行的,就是这么调用,放到本地掉就完了

结果一执行,完蛋,卧槽

报错的意思就是,这个对象没有sign属性,很奇怪啊,在控制台都可以用的,在node里不能用,那说明有检测环境的,把那个检测环境的部分改下试试,先把代码缩一下:

先看那个三目运算,复制到控制台执行看看:

实际就还是window,那就改成window:

卧槽,这里才看到是jsvmp啊,这他妈,大名鼎鼎啊,说实话我有点慌了,本篇博文到此结束?

不不不,还是要挣扎下的,先把它当作普通的函数看待,先看,上面主要的两段代码,第一段是定义,第二段是调用,最后的console打印是我自己加的

那行,那看看参数有没有问题:

定义的时候用的b,e,f

看看下面穿的参数是啥,好家伙,不看不知道,一看传了这么多的东西,用sublime 打开看到:

上面一大段全是b变量,后面的中括号里的值,最后会变成e和f变量,而这里面又有三目运算符

把三目运算符整理下,先看第一个:

 那说明,这个三目运算符就是void 0了,把这相关的都替换成void 0,搜索看,只有一个,

 替换之后再执行,貌似刚才那个sign属性解决了,但是又出现了新的错

这个一看还是补环境的问题了,把这个补了,href是location对象里的,补完又发现新的报错

到这一步的时候,因为报length的话,大概率是补环境除了问题,那么说明刚才的href没有补对,那我们直接再目标网站的控制台copy一下,

 回车即可,一定要在目标网站的控制台里copy,copy完执行,至少当前的问题解决了,再搞新的问题

这个userAgent就再熟悉不过了,咋办呢?也直接copy吧,因为userAgent属于navigator,直接如下copy:

 再次强调,目标网站控制台里执行

copy完放到代码里执行看看:

发现,卧槽,终于tmd没报错,而且有结果了

但是,这个长度好像不大对劲,短这么多,好像差点啥,到底差什么东西呢

仔细推敲,网上也查了相关的,有说补齐cookie的,我补齐之后执行的结果还是很短,所以,应该还有什么东西没有注意到的

在目标网站的控制台里执行,就是可以拿到很长的字段,这就很骚了

 

那我觉得应该还是环境的问题,应该有个我们忽视了的地方

先打印window看看:

location基本没有太大区别:

 不一样的主要是window.document和window.navigator,以及window.localStorage,但是恰恰这三个对象是没法直接copy的:

因为你发现,粘贴出来的要嘛是undefined,要嘛是{}:

 

这他妈就很秀了,难道这就是jsvm的威力吗?

我另开一个浏览器标签,把刚才抠出来的代码放到控制台执行,然后测试看看:

 首先,至少说明,补的基础环境没问题,就差一些特征值了

再看,目标控制台里的这个arguments,

新开的控制台的这个arguments的值:

所以这里就看出区别了,目标控制台里多了个这个:

但是就不知道是不是这里不同导致的原因了。

跟着断点接着走,新开控制台:

看这个c值

 

目标的控制台里的:

看这个c值:

 差距也太大了,而且c就是window.document对象,也就是上面没法copy对象其一

而且,新开标签页,走到后面进入到了这里:

目标控制台,进入到了这里:

能走不一样的原因就是,这里的B[e]

 目标控制台的B[e]是空的,所以,G穿的最后一个值是0,而新开控制台的B[e]有值,所以传的最后一个参数是1,也就导致上面走了不同的逻辑

那么这个B到底是啥:

我去,这他妈的,最后经过我的调试,发现,主要是穿的这个参数的不同:

 导致取值不同,上面是目标控制台的,下面是新标签页控制台的:

但是这一个值的变化,不是我们能控制的,写死也是没用的,唉,这就是jsvmp的强大吗?唉,想想后怕了。

但是中途放弃不是我的作风,我就不信了,我开始在漫无目的的找特征,回到最底部调用部分看这个window对象,突然的看到localStorage部分,我激动了,这个是目标网站里的:

新标签的控制台,window部分,明显感觉有问题对吧,

但是,刚才我们分析的,就是那个this里的window对象不一样,那么我们尽量的去贴靠原网站的window里需要的值,我们给赋值下localstorage,用copy看看呢:

哎,发现这个倒是可以复制哈

把这段封装成一个自执行函数:

放到新标签的控制台里,然后放到控制台里执行,回车,再访问下验证是否成功了,发现可行的

 好,现在再在控制台执行下sign:

把localStorage部分放进去之后,再次执行:

卧槽,说实话,有点小激动,至少这个长度看着很像了,就不知道能不能用了,在代码里看看呢?

直接复制刚才的sign生成好的字段,执行测试,卧槽,牛逼啊,数据结构终于有了

还没完哈,现在要搞一个在本地能够直接生成的,而不是每次生成需要去浏览器的控制台执行再复制出来的

当把那段自治性函数放到node环境里执行的时候:

很奇怪,提示的是没有setItem这个属性,这咋办呢?

尝试jsdom

理一下思路,目前就差一个localstorage的赋值了,但是上面的代码的setItem无法用,因为localstorage对象是window对象里的,那么我们直接用node伪造一个window吧,咋伪造呢?用jsdom,安装nodejs就不说了,网上一堆教程

在本地搞一个node项目,npm init命令初始化后,然后执行命令npm install jsdom安装jsdom,具体过程也省略了,网上教程同样一堆

现在创建一个js文件,把window对象引入,这里注意一下,因为我们要用localstorage,new JSDOM的时候必须要给个主域名,不然没法用localstorage

测试下现在setItem成功没有:

ok了,现在把刚才抠出来的代码整合到一起,结果出现了这个,卧槽,心累啊

说明这段代码还验证了其他很多东西导致这个sign属性没有正常赋值,换路子吧

本地html文件生成

把抠出来的代码放到一个html文件里,同时要注意的,url里的时间戳,必须要跟sign生成时传进去的url里的时间戳保持一致,不然用不了

ok,用pycharm自带的轻量服务器执行查看:

点击那个谷歌浏览器图标,自动打开并展示如下页面:

拿到这两个值去请求测试,哭了,这他妈终于有数据了

那会过头想想,这个没法运用到实际啊,这个第一,还是用了浏览器自带的window对象,第二,这个服务端是pycharm,还不好控制,就算把这一部换成flask,用requests去请求这个接口,拿结果?也不行啊,为啥,因为请求拿到的是源码,这里的时间戳和sign是js生成的

要走这条路的话,只有用浏览器驱动,puppeter或者selenium了,那这里就会有人说了,这都上浏览器驱动了,那还抠啥代码啊,直接一开始就用浏览器驱动了呗,是的,所以这套路也不是我喜欢的

怎么办

构造localStorage对象

上面试了两个路子都不行,那究其原因就还是那个setItem没法用,没法用的原因是我们构造的window对象里的localStorage不是正确的对象,这里构造一个出来,行不行呢?理论上是可以,试下:

1

2

3

4

5

6

7

8

9

10

11

window.localStorage = {

    removeItem: function (key) {

        delete this[key]

    },

    getItem: function (key) {

        return this[key] ? this[key]: null;

    },

    setItem: function (key, value) {

        this[key] = "" + value;  // 将数字转为字符串

    },

};

把这段代码放到最开始抠出来的代码里,并在执行setItem前面,代码如下:

执行看看,激动万分啊,这个长度,看着就跟目标网站出来的sign长度一致了

放到程序里执行测试:

执行,哇的一声就哭出来了,ok

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值