!已失效!
抓包分析
获取验证码
获取加密公钥
其中hash是变化的,公钥key不变
登录
其中用户名没有被加密,密码被加密。
因为在获取公钥的时候同时返回了一个hash值,推测此hash值与密码加密有关。
通过谷歌浏览器控制台分析js代码
右键登录按钮,检查,查看 Event Listeners ,点击a.btn.btn-login右边的login.4f030c3....js:6查看js代码。
点击左下角的{}展开代码
因为获取公钥和hash的链接中有action=getkey,尝试在源代码中搜索getkey,于是找到这样一个函数:
1 encryptPassword: function (e, t) { 2 var n = this, 3 r = !1; 4 return $.ajax({ 5 url: "https://passport.bilibili.com/login?act=getkey&r=" + Math.random(), 6 async: !1 7 }).done(function (t) { 8 t && t.error && (n.publicTip = "登录失败,服务端出现异常", e = null); 9 var i = new JSEncrypt; 10 i.setPublicKey(t.key); 11 var a = i.encrypt(t.hash + e); 12 e = a, 13 r = !0 14 }), 15 r ? e : "" 16 }
观察第10、11行,可知hash和变量e相加后被RSA加密,现在需要确定e是什么。在第11行代码所在的位置设置一个断点,输入用户名密码验证码之后点击登录,在这里密码框输入的是‘mypassword’
点击e就会显示出e的值,看来e就是密码明文。
于是,可以猜测,加密的过程是:密码字符串前面串接hash值,然后进行RSA加密。下面通过测试检查猜测是否正确。
模拟登录
每一次请求的headers都完全复制抓包的内容。
1.获取验证码
1 def get_code(): 2 url='https://passport.bilibili.com/captcha?r=0.1265352187487443' 3 headers={复制抓包到的headers} 4 session.headers.clear() 5 session.headers.update(headers) 6 r=session.get(url) 7 file=open('code.jpg','wb') 8 file.write(r.content) 9 file.close()
2.getkey
1 def get_key(): 2 url='https://passport.bilibili.com/login?act=getkey&r=0.4365052982637341' 3 headers={复制抓包到的headers} 4 session.headers.clear() 5 session.headers.update(headers) 6 r=session.get(url) 7 jsondata=json.loads(r.text) 8 #hash变化,key不变 9 return (jsondata['hash'],jsondata['key'])
3.加密,具体可以参考博客园的模拟登录的附录部分
1 def encrypt(Hash,key,password): 2 from Crypto.PublicKey import RSA 3 from Crypto.Cipher import PKCS1_v1_5 4 from base64 import b64encode 5 encryptor=PKCS1_v1_5.new(RSA.importKey(bytes(key,'utf-8'))) 6 return str(b64encode(encryptor.encrypt(bytes(Hash+password,'utf-8'))),'utf-8')
4.登录
1 def login(code,username,password): 2 #注意,在登录页面中验证码会自动转成大写 3 url='https://passport.bilibili.com/web/login' 4 headers={复制抓包到的headers} 5 data={'cType':'2', 6 'vcType':'1', 7 'captcha':code, 8 'user':username, 9 'pwd':password, 10 'keep':'true', 11 'gourl':'http://www.bilibili.com/'} 12 session.headers.clear() 13 session.headers.update(headers) 14 r=session.post(url,data=data) 15 return r.text
如果登录成功,将返回 "code":0
5.测试
testurl='https://www.bilibili.com/account/dynamic'
在未登录时,testurl的标题为“bilibili - 提示”,登录时标题为“哔哩哔哩 (゜-゜)つロ 干杯~-bilibili”
session.headers.clear() r=session.get(testurl) p=r.text.find('<title>')+len('<title>') print(r.text[p:r.text.find('<',p)])