共计 3771 个字符,预计需要花费 10 分钟才能阅读完成。
关注微信公众号:K 哥爬虫,QQ 交换群:808574309,继续分享爬虫进阶、JS/ 安卓逆向等技术干货!
申明
本文章中所有内容仅供学习交换,抓包内容、敏感网址、数据接口均已做脱敏解决,严禁用于商业用途和非法用处,否则由此产生的所有结果均与作者无关,若有侵权,请分割我立刻删除!
逆向指标
- 指标:某 e 网通登录接口
- 主页:aHR0cHM6Ly93ZWIuZXd0MzYwLmNvbS9yZWdpc3Rlci8jL2xvZ2lu
- 接口:aHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA==
-
逆向参数:
- Request Headers:
sign: 3976F10977FC65F9CB967AEF79E508BD
- Request Payload:
password: "A7428361DEF118911783F446A129FFCE"
- Request Headers:
逆向过程
抓包剖析
来到某 e 网通的登录页面,轻易输出一个账号密码登陆,抓包定位到登录接口为 aHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA==,申请头里,有一个 sign,Payload 里,明码 password 被加密解决了。
参数逆向
sign
首先来看一下申请头的 sign,尝试间接搜寻一下,发现并不是通过某些申请返回的数据,察看一下其余申请,能够发现同样有 sign,而且每次申请的值都不一样:
由此能够初步判断这个值应该是通过 JS 生成的,全局搜寻关键字 sign:
,能够别离在 request.js、request.ts 两个文件外面看到疑似 sign 赋值的中央,埋下断点调试,胜利断下,原理也很简略,工夫戳加上一串固定的字符,通过 MD5 加密后再转大写即可。
应用 Python 实现:
import time
import hashlib
timestamp = str(int(time.time() * 1000))
sign = hashlib.md5((timestamp + 'bdc739ff2dcf').encode(encoding='utf-8')).hexdigest().upper()
print(sign)
password
password 是明文明码通过加密后失去的值,如果尝试间接去搜寻的话,会发现进去的值十分十分多,要想找到精确的值难度微小:
能够看到这条申请是 XHR 申请,本次咱们应用 XHR 断点的办法来定位具体的加密地位,通过本次案例,咱们来学习一下具体是如何跟进调用栈、如何通过上下文来定位具体的加密地位。
切换到 Network 选项卡,找到登陆申请,鼠标挪动到 Initiator 选项卡下的 JS 上,能够看到其调用栈,如果站点的加密形式比较简单,没有太多混同的话,调用栈外面就能够看到 login、send、post、encrypt 等等之类的关键词,这种状况下就能够间接点进去,比拟容易找到加密的中央,然而大多数站点对于函数名、变量名都做了混同,和本案例一样,调用栈外面显示的都是一些单个或者多个无规则的字母的函数,无奈间接定位,此时就须要咱们从最初一个函数往前缓缓找。
点击进入最初一个函数,即 Y 函数,它位于调用栈的最顶层,示意通过此函数后,浏览器就会发送登录的申请,明码的加密过程曾经处理完毕。在此函数埋下断点,能够在右侧的 Call Stack 看到调用栈,从下到上,示意的是点击登陆后,先后调用的函数的执行过程:
想要找到具体的加密地位,咱们就要顺次往前找,挨个函数进行剖析,例如往前定位到倒数第二个调用栈,即 o 函数,能够看到传进来的 params 参数外面就蕴含了已加密的明码信息,这阐明加密操作必定在此函数之前:
依据这种思路,一步一步往下跟进调用栈,能够看到在 utils.ts 外面执行了一个匿名函数,其中调用了一个 passwordEncrypt 函数,通过函数名就能够看出基本上就是明码加密的函数了:
在此处埋下断点进行调试,传进来的是明文明码,passwordEncrypt 实际上是调用的 encode.ts 中的 O 函数:
跟进 O 函数,援用了 crypto-js 加密模块,很显著的 AES 加密,本地改写一下就行了。
本次的案列加密比较简单,然而加密函数暗藏得比拟好,须要急躁跟进调用栈,通过间接搜寻的话,后果太多,是不太容易定位加密函数的,本次案例中跟进到一个函数后,能够很分明的看到加密的中央,那么有的站点可能混同得更加厉害,是看不出来有加密函数的,这种状况下就须要咱们留神参数的变动状况,如果在这个调用栈看到的是加密后的参数,在上一个调用栈外面看到的是明文的参数,那么加密的操作必然在这两个调用栈之间,埋下断点,仔细分析即可。
残缺代码
GitHub 关注 K 哥爬虫,继续分享爬虫相干代码!欢送 star!https://github.com/kgepachong/
以下只演示局部要害代码,不能间接运行! 残缺代码仓库地址:https://github.com/kgepachong…
JavaScript 加密代码
CryptoJS = require("crypto-js")
const key = CryptoJS.enc.Utf8.parse("20171109124536982017110912453698");
const iv = CryptoJS.enc.Utf8.parse('2017110912453698'); // 十六位十六进制数作为密钥偏移量
function getEncryptedPassword(word) {let srcs = CryptoJS.enc.Utf8.parse(word);
let encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.ciphertext.toString().toUpperCase();
}
// 测试样例
// console.log(getEncryptedPassword("123457"))
Python 登录代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import hashlib
import execjs
import requests
login_url = '脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler'
session = requests.session()
def get_sign():
timestamp = str(int(time.time()*1000))
sign = hashlib.md5((timestamp + 'bdc739ff2dcf').encode(encoding='utf-8')).hexdigest().upper()
return sign
def get_encrypted_parameter(password):
with open('ewt360_encrypt.js', 'r', encoding='utf-8') as f:
ewt360_js = f.read()
encrypted_password = execjs.compile(ewt360_js).call('getEncryptedPassword', password)
return encrypted_password
def login(sign, username, encrypted_password):
headers = {
'sign': sign,
'timestamp': str(int(time.time()*1000)),
'sec-ch-ua': '"Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
data = {
'autoLogin': True,
'password': encrypted_password,
'platform': 1,
'userName': username
}
response = session.post(url=login_url, headers=headers, json=data)
print(response.json())
def main():
username = input('请输出登录账号:')
password = input('请输出登录明码:')
sign = get_sign()
encrypted_password = get_encrypted_parameter(password)
login(sign, username, encrypted_password)
if __name__ == '__main__':
main()