导言
在Web开发中后端负责程序架构和数据管理,前端负责页面展示和用户交互;在这种前后端分离的开发方式中,以接口为标准来进行联调整合,为了保证接口在调用时数据的安全性,也为了防止请求参数被篡改,大多数接口都进行了请求签名、身份认证、动态Cookie等机制。另外部分网站会对返回的数据进行加密,通常利用AES、RSA等加密方式,也有在传输时对数据进行序列化,比如Protobuf等。
参数的生成方法都是由Js来控制的,如果要想直接从接口上获取数据,就要去调试分析JavaScript的调用逻辑、堆栈调用关系来弄清楚网站加密的实现方法,根据网站的参数生成规则还原加密参数,可以称这个过程为Js逆向。
目前加密参数常用的逆向方式:
- 根据源码的生成逻辑还原加密代码
- copy源码补环境模拟加密参数生成
- 通过RPC的方式远程调用
一,JavaScript基础
标识符与运算符:
语句标识符:
Throw 抛出异常
const 声明固定值的变量
let 声明块作用域的变量
var 声明变量
this 当前所属对象逻辑运算符:
&& and
|| or
! not位运算符:
& 如果两位都为1,则设置每位为1
| 如果两位之一为1,则设置每位为1
~ 反转所有位
^ 异或运算
<<< 零填充左位移
>>> 零填充右位移
>> 有符号右位移
三元运算:
v1 = 条件 ? 值A : 值B;res = 1 === 1 ? 99 : 88;
console.log(res); // 99 先执行1 === 1
赋值和比较:
v1 = 11 === (n=123)console.log(n); // 123
console.log(v1); // false
逻辑运算:
v1 = true || true
v2 = 9 || 14 # v2=9
v3 = 0 || 15 # v3=15
v4 = 0 || 15 || "武沛齐" # v4=15最终结果取决那个值,结果就是那个值
举例:
var res = (null === (n = window.byted_acrawler) || void 0 === n || null === (a = n.sign) || void 0 === a ? void 0 : a.call(n, o)) || ""o = {"url":"...."}
n = window.byted_acrawler -> 确定不为空
a = window.byted_acrawler.sign -> 确定不为空
window.byted_acrawler.sign.call(n, i) -> 确定不为空var res = window.byted_acrawler.sign.call(n, o)
函数中的this:
function sign(v1){// this 默认内部会有个this值console.log(v1);
}
// 执行,如果没有为this赋值,函数内部默认 this=window
sign(456)// call的方式执行,函数内部 this=123
sign.call(123,456)// 若函数内部没有使用this,那sign(456)和sign.call(123,456)这两种写法没有差别
// 举例
var o = window.byted_acrawler.sign.call(n,i)
var o = window.byted_acrawler.sign(i)// n就会传递给sign函数中this
// i当做参数传递
arguments接收函数的参数:
function sign(){console.log(arguments)
}sign()
sign(11,22,33)
sign(11,22,44,55)// [Arguments] {}
// [Arguments] { '0': 11, '1': 22, '2': 33 }
// [Arguments] { '0': 11, '1': 22, '2': 44, '3': 55 }
添加键值对:
v1 = { k1:123 }
v2 = { k2:99, k3:888}Object.assign(v1, v2) // 将v2的键值对添加到v1中console.log(v1) // {k1: 123, k2:99, k3:888 }
console.log(v2) // { k2: 99, k3: 888 }
属性从一个或多个源对象复制到目标对象,返回修改后的对象
可以运用到补充环境:
extra = { k2:99, k3:888}
Object.assign(global, extra) 如果global指全局变量,那么可以直接使用如下写法:
console.log(k2)
console.log(k3)
作用域:
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
Js的作用域分为三种:全局作用域、函数作用域、块级作用域。
全局作用域可以让用户在任何位置进行调用,最外层函数和在最外层函数外面定义的变量拥有全局作用域,所有未定义直接赋值的变量会自动声明为拥有全局作用域(如 kfk = “kte”),所有window对象的属性也拥有全局作用域。
函数作用域只有在函数内部可以被访问。
块级作用域则是在if和switch的条件语句或for和while的循环语句中。
return 返回多个值:
此时实际返回的是最后一个的值,前面的都是在做运算。
在JS中有多种取值方法:
JSONP:
JSONP的由来:
1、Ajax直接请求普通文件存在跨域无权限访问的问题,不管是静态页面、动态页面、web服务,只要是跨域请求,一律不准。
2、不过我们发现,web页面调用js文件则不受跨域的影响(不仅如此,我们还发现凡是拥有“src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>。
3、于是可以判断,当前阶段如果想通过纯web端跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。
4、恰巧我们知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据。
5、这样,解决方案就呼之欲出了,web服务端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件,显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装进去。
6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来很像ajax,但其实并不一样。
7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,简称JSONP。该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名包裹在JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。示例:https://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403。
总结:jsonp,为了解决浏览器同源策略引发的跨域问题。格式:名字 ( {“code”:0,“message”:“0”,“ttl”:1} )
如果向某个站发送请求,获取到JSONP格式的数据,怎么处理?
- 切片+序列化
- replace
- eval内置函数对数据进行编译(当成代码来编译)
def funcname(info): # 定义对JSONP的处理函数print(type(info), info)v1 = 'funcname( {"code":0,"message":"0","ttl":1} )'
eval(v1)
二,Window对象属性
Window对象表示浏览器当前打开的窗口,有以下常用属性:
document history location navigator screen
Document,载入浏览器的HTML文档。
cookie 当前cookie
domain 文档域名
referrer 访问来源
title 文档标题
URL 当前URL
Navigator对象包含的属性描述了当前使用的浏览器,可以使用这些属性进行平台专用的配置。
userAgent 用户代理
webdriver 是否启用驱动
AppName 浏览器名称
Location对象包含有关当前URL的信息。
host 当前主机名和端口号
href 当前URL
protocol 当前URL的协议
Screen对象中存放着有关显示浏览器屏幕的信息。
height 显示屏高度
width 显示屏宽度
History对象包含用户在浏览器窗口中访问过的URL。
length 浏览器历史列表中的URL数量
back() 加载前一个URL
go() 加载某个具体页面
三,chrome开发者工具介绍
如何调出浏览器开发者工具:
- F12(Fn+F12)
- 右键检查
- 右上角->更多工具->开发者工具
下面介绍chrome开发者工具最常用的5个功能模块:
Elements
元素,是经过了浏览器和JS渲染后的代码,与页面源代码不一样
用于查看或修改HTML元素的属性,CSS属性,监听事件,断点等。CSS可以即时修改,即时显示,大大方便了开发者调试页面,可以右击鼠标,对Elements中的属性进行操作
查看元素的CSS属性:在元素的右边栏中的styles页面可以查看该元素的CSS属性,这个页面展示该元素原始定义的CSS属性以及从父级继承的CSS属性。从这个页面还可以查到该元素的某个CSS特性来自那个CSS文件,使编码调试时修改代码变得十分方便
在Styles页旁边,有一个Computed页面,这个页面展示该元素经过计算之后的所有CSS属性,即最后浏览器渲染页面时使用的属性,属性的计算由浏览器自动进行,是浏览器渲染页面的一个必不可少的过程
Event Listeners 中显示了选择节点当前绑定的事件:
Console
控制台,一般用于执行一次性代码,查看JavaScript对象,查看调试日志信息或异常信息。还可以当作JavaScript API查看用,例如想查看console都有那些方法和属性,可以直接输入"console"并执行
Sources
源代码:该页面可以查看页面的HTML源代码,JavaScript源代码,CSS源代码,此外最重要的是可以调试JavaScript源代码,可以给JS代码添加断点等;按列分为三列,从左至右分别是文件列表区、当前文件区、断点调试区
文件列表区中有Page、Snippets、FileSytem等。Page可以看到当前所在的文件位置,在Snippets中单击New Snippets可以添加自定义的Js代码,FileSytem可以把本地的文件系统导入到chrome中
Call Stack:断点的调用堆栈列表,完整地显示了导致代码被暂停的执行路径
Scope:当前断点所在函数执行的作用域内容
Network
网络:就是爬虫中的抓包工具,主要用于查看header等与网络连接相关的信息
过滤按钮负责打开或关闭过滤面板,其中Filter框中是对url进行搜索,XHR:ajax数据,即异步加载数据
🔍按钮:其实就是ctrl+F调出的全局搜索按钮,相比Filter框搜索,🔍搜索得更细
Preserve log:保留日志,当选择保留日志,重新加载url界面时,之前请求显示的资源信息,会保留下来
Disable cache:禁用缓存,当关闭这个开关,则页面资源会存入缓存,以后再有请求时,如果缓存中已有,则不会再向浏览器发送请求
Application
Application是应用管理部分,主要记录网站加载的所有资源信息,包括储存数据(Local Storage, Session Storage, Cookies等)、缓存数据、字体、图片、样式、脚本等。
有时调试需要清除Cookies,可以在Application的Cookies位置单击鼠标右键,选择Clear进行清除,或者根据Cookies中指定的Name和Value进行清除。
注意:在辨别cookie来源时,可以看到httpOnly一栏,有√的是来自服务端,没有√的则是本地生成的。