逆向模拟登录

文章目录

  • 逆向模拟登录
    • 案例介绍
    • 滑块验证
      • 思路分析
      • 代码实现
        • 加密函数
        • 获取uid与cookies
        • 构建slideInfo并加密
        • 模拟完成滑块验证
        • 结果
    • 获取验证码
      • 思路分析
      • 代码整合
        • 获取bella
        • 发送请求
        • 代码总结
    • 登录

逆向模拟登录

案例介绍

网站:https://user.qunar.com/passport/login.jsp

用爬虫模拟完成滑块验证+获取验证码并登录

逆向模拟登录_第1张图片

逆向模拟登录_第2张图片

滑块验证

思路分析

1)抓包可得在发送验证码前需要先完成滑动验证并获取slideToken

逆向模拟登录_第3张图片

2)分析滑块验证码的请求包

逆向模拟登录_第4张图片

可见请求体中的data需要进行某种算法加密,而其他三个参数为定值,接下来我们就需要找到这个算法并用python代码模拟加密过程。

3)查看调用程序并获取算法

逆向模拟登录_第5张图片

逆向模拟登录_第6张图片

在调用程序中可以看到传递的data的值为a,而a = t.encryption()。

逆向模拟登录_第7张图片

搜索encryption可得该加密算法先对sliderInfo进行序列化,后采用AES算法进行加密

逆向模拟登录_第8张图片

在此处打个断点,重新完成滑块验证即可得到sliderInfo的信息:

acc: []

deviceMotion: [DeviceOrientationEvent, DeviceOrientationEvent, DeviceOrientationEvent, DeviceOrientationEvent, DeviceOrientationEvent, DeviceOrientationEvent, DeviceOrientationEvent, DeviceOrientationEvent]

endTime: 1707832186251

openTime: 1707832184833

ori: []

startTime: 1707832186072

track:  ['86143;143.00;334.00;1.00', '86162;151.00;338.00;9.00', '86182;173.00;343.00;31.00', '86202;228.00;347.00;86.00', '86223;336.00;349.00;194.00', '86243;508.00;349.00;366.00']

uid: "0001028025405c0d0a609b28"

userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
  • openTime,startTime,endTime分别为页面打开时间,滑块开始滑动的时间,滑动结束的时间
  • userAgent为用户主机信息
  • track为滑动轨迹,可以设置为定值,也可以进行模拟获取(为简单起见,本例中设置为定值)
  • deviceMotion数组中间包含一串重复的信息,可用 [{“isTrusted”: True}, {“isTrusted”: True},……]来模拟

uid似乎需要动态获取

4)获取uid

逆向模拟登录_第9张图片

搜索t.sliderInfo查找uid的来源,可见uid = h(“QN1”)

逆向模拟登录_第10张图片

跳转到函数h可得,uid似乎在cookie中

逆向模拟登录_第11张图片

查看cookie可得uid就是cookie中的QN1,因此,我们需要先模拟访问登录界面并获取到QN1的值。

代码实现

加密函数
def aes_encrypt(data_string):
    # key = "227V2xYeHTARSh1R".encode('utf-8')
    key_string = "32323756327859654854415253683152"
    key = binascii.a2b_hex(key_string)

    aes = AES.new(
        key=key,
        mode=AES.MODE_ECB
    )
    raw = pad(data_string.encode('utf-8'), 16)
    aes_bytes = aes.encrypt(raw)
    res_string = base64.b64encode(aes_bytes).decode('utf-8')
    return res_string
获取uid与cookies
res = requests.get("https://user.qunar.com/passport/login.jsp")
cookie_dict = res.cookies.get_dict()
cookie_qn1 = cookie_dict['QN1']
构建slideInfo并加密
sliderInfo = {
    "openTime": int((time.time() - random.randint(500, 3000)) * 1000),
    "startTime": int((time.time() - random.uniform(2, 4)) * 1000),
    "endTime": int((time.time() - random.uniform(0, 1)) * 1000),
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
    "uid": cookie_qn1,
    "track": ["95339;72.00;241.00;1.00", "95359;104.00;241.00;33.00", "95380;195.00;241.00;124.00",
              "95400;358.00;249.00;287.00"],
    "acc": [],
    "ori": [],
    "deviceMotion": [{"isTrusted": True}, {"isTrusted": True}, {"isTrusted": True}]
}

data_string = json.dumps(sliderInfo, separators=(',', ':'))
data = aes_encrypt(data_string)
模拟完成滑块验证
res1 = requests.post(
    url="https://vercode.qunar.com/inner/captcha/snapshot",
    json={
    "appCode": "register_pc",
    "cs": "pc",
    "data": data,
    "orca": 2
},
    cookies=cookie_dict
)
res1 = json.loads(res1.text)
print(res1)
结果

逆向模拟登录_第12张图片

获取验证码

在滑块验证通过后的都的”cst“的值即为sildeToken,但还有bella的值需要获取

逆向模拟登录_第13张图片

思路分析

1)寻找bella

逆向模拟登录_第14张图片

点开调用程序,无法确定bella的来源,因此我们直接在代码中搜bella,查看bella在哪里被赋值

逆向模拟登录_第15张图片

可见bella被赋值为f(若不确定,可以在此处打个断点,查看此处f的值是否与请求中的bella值相同),而f似乎为一个全局函数

2)模拟获取bella的值

逆向模拟登录_第16张图片

跳转到Bella所在的js文件,将该文件全部copy到我们自己目录下的js文件(共17000多行,但不要慌)

创建一个html文件,先模板在网页中获取bella

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js">script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js">script>

head>
<body>
<script src="sdk.js">script> # 引入copy过来的js文件
<script>
    console.log(window.Bella({slideToken: "654b163304abc177cb360da79562b1f7"}));
script>
body>

html>

打开浏览器,即可在控制台中看到bella的值与我们需要的似乎一致

请添加图片描述

3)用python代码获取bella

用python调用node执行js代码时需要先下载相关依赖并在js文件中添加浏览器环境

npm install -g jsdom       
npm install -g node-gyp 
npm install -g canvas --canvas_binary_host_mirror=https://npm.taobao.org/mirrors/canvas/
const jsdom = require("jsdom");
const {JSDOM} = jsdom;
XMLHttpRequest = function () {
    return {
        open: function () {
        },
        send: function () {
        },
        onreadystatechange: function () {
        }
    };
};  // 若报XMLHttpRequest相关的错误需要在此处继续添加相关函数
const html = `

Hello world

`; const dom = new JSDOM(html, { url: "https://user.qunar.com/passport/login.jsp", referrer: "https://www.qunar.com/", contentType: "text/html" }); document = dom.window.document; window = global; Object.assign(global, { location: { hash: "", host: "user.qunar.com", hostname: "user.qunar.com", href: "https://user.qunar.com/passport/login.jsp", origin: "https://user.qunar.com", pathname: "/passport/login.jsp", port: "", protocol: "https:", search: "", }, navigator: { appCodeName: "Mozilla", appName: "Netscape", appVersion: "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36", cookieEnabled: true, deviceMemory: 8, doNotTrack: null, hardwareConcurrency: 4, language: "zh-CN", languages: ["zh-CN", "zh"], maxTouchPoints: 0, onLine: true, platform: "MacIntel", product: "Gecko", productSub: "20030107", userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36", vendor: "Google Inc.", vendorSub: "", webdriver: false } });

在配置完环境后执行测试代码

import subprocess

res = subprocess.check_output('node sdk.js', shell=True)
data_string = res.decode('utf-8')
print(data_string)

会发现程序卡住无法获取结果,此时我们猜测可能是因为我们缺少js文件中的某些数据导致程序无法执行完,但缺少的数据与我们需要的Bella函数无关,我们阻止其执行即可

逆向模拟登录_第17张图片

我们搜索bella可得,在9804行时bella就已经被赋值(_0x47bb39为一个函数),因此无需执行后面的代码。

逆向模拟登录_第18张图片

再次执行测试代码,即可发现能够正常执行完成并拿到bella值

import subprocess

res = subprocess.check_output('node sdk.js eeb08c57bf8fc82f5c9fd5163293cf38', shell=True)
data_string = res.decode('utf-8')

print(data_string)

逆向模拟登录_第19张图片

代码整合

获取bella
import subprocess
token = res1['data']['cst']
res = subprocess.check_output(f'node sdk.js {token}', shell=True)
data_string = res.decode('utf-8').strip()
发送请求
res2 = requests.post(
    url="https://user.qunar.com/weblogin/sendLoginCode",
    data={
        "mobile": "自己的手机号",
        "prenum": "86",
        "loginSource": '1',
        "slideToken": token,
        "smsType": "0",
        "appcode": "register_pc",
        "bella": data_string
    },
    cookies=cookie_dict
)
print(res2.text)
代码总结
import base64
import binascii
import json
import random
import subprocess
import time

import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad


def aes_encrypt(data_string):
    # key = "227V2xYeHTARSh1R".encode('utf-8')
    key_string = "32323756327859654854415253683152"
    key = binascii.a2b_hex(key_string)

    aes = AES.new(
        key=key,
        mode=AES.MODE_ECB
    )
    raw = pad(data_string.encode('utf-8'), 16)
    aes_bytes = aes.encrypt(raw)
    res_string = base64.b64encode(aes_bytes).decode('utf-8')
    return res_string


res = requests.get("https://user.qunar.com/passport/login.jsp")
cookie_dict = res.cookies.get_dict()
cookie_qn1 = cookie_dict['QN1']

sliderInfo = {
    "openTime": int((time.time() - random.randint(500, 3000)) * 1000),
    "startTime": int((time.time() - random.uniform(2, 4)) * 1000),
    "endTime": int((time.time() - random.uniform(0, 1)) * 1000),
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
    "uid": cookie_qn1,
    "track": ["95339;72.00;241.00;1.00", "95359;104.00;241.00;33.00", "95380;195.00;241.00;124.00",
              "95400;358.00;249.00;287.00"],
    "acc": [],
    "ori": [],
    "deviceMotion": [{"isTrusted": True}, {"isTrusted": True}, {"isTrusted": True}]
}

data_string = json.dumps(sliderInfo, separators=(',', ':'))
data = aes_encrypt(data_string)
res1 = requests.post(
    url="https://vercode.qunar.com/inner/captcha/snapshot",
    json={
        "appCode": "register_pc",
        "cs": "pc",
        "data": data,
        "orca": 2
    },
    cookies=cookie_dict
)
res1 = json.loads(res1.text)
token = res1['data']['cst']

res = subprocess.check_output(f'node sdk.js {token}', shell=True)
data_string = res.decode('utf-8').strip()
res2 = requests.post(
    url="https://user.qunar.com/weblogin/sendLoginCode",
    data={
        "mobile": "13720824626",
        "prenum": "86",
        "loginSource": '1',
        "slideToken": token,
        "smsType": "0",
        "appcode": "register_pc",
        "bella": data_string
    },
    cookies=cookie_dict
)
print(res2.text)

此时我们的手机就能收到验证码了,之后将验证码携带到登录请求中即可完成登录

登录

import base64
import binascii
import json
import random
import subprocess
import time

import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad


def aes_encrypt(data_string):
    # key = "227V2xYeHTARSh1R".encode('utf-8')
    key_string = "32323756327859654854415253683152"
    key = binascii.a2b_hex(key_string)

    aes = AES.new(
        key=key,
        mode=AES.MODE_ECB
    )
    raw = pad(data_string.encode('utf-8'), 16)
    aes_bytes = aes.encrypt(raw)
    res_string = base64.b64encode(aes_bytes).decode('utf-8')
    return res_string


res = requests.get("https://user.qunar.com/passport/login.jsp")
cookie_dict = res.cookies.get_dict()
cookie_qn1 = cookie_dict['QN1']

sliderInfo = {
    "openTime": int((time.time() - random.randint(500, 3000)) * 1000),
    "startTime": int((time.time() - random.uniform(2, 4)) * 1000),
    "endTime": int((time.time() - random.uniform(0, 1)) * 1000),
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
    "uid": cookie_qn1,
    "track": ["95339;72.00;241.00;1.00", "95359;104.00;241.00;33.00", "95380;195.00;241.00;124.00",
              "95400;358.00;249.00;287.00"],
    "acc": [],
    "ori": [],
    "deviceMotion": [{"isTrusted": True}, {"isTrusted": True}, {"isTrusted": True}]
}

data_string = json.dumps(sliderInfo, separators=(',', ':'))
data = aes_encrypt(data_string)

res1 = requests.post(
    url="https://vercode.qunar.com/inner/captcha/snapshot",
    json={
        "appCode": "register_pc",
        "cs": "pc",
        "data": data,
        "orca": 2
    },
    cookies=cookie_dict
)
res1 = json.loads(res1.text)
token = res1['data']['cst']

res = subprocess.check_output(f'node sdk.js {token}', shell=True)
data_string = res.decode('utf-8').strip()
mobile = input("请输入手机号:")
res2 = requests.post(
    url="https://user.qunar.com/weblogin/sendLoginCode",
    data={
        "mobile": mobile,
        "prenum": "86",
        "loginSource": '1',
        "slideToken": token,
        "smsType": "0",
        "appcode": "register_pc",
        "bella": data_string
    },
    cookies=cookie_dict
)

if (res2.json()['errcode'] == 200):
    print("验证码发送成功")
    sms_code = input("请输入短信验证码:")
    res3 = requests.post(
        url="https://user.qunar.com/weblogin/verifyMobileVcode",
        json={
            "piccoloT": "login_register_pc",
            "mobile": mobile,
            "prenum": "86",
            "vcode": sms_code,
            "type": "3",
            "slideToken": token,
            "appcode": "register_pc",
            "loginSource": 1,
        },
        cookies=cookie_dict
    )
    print(res3.text)
else:
    print("验证码发送失败")

登录结果:

逆向模拟登录_第20张图片

你可能感兴趣的:(python爬虫,爬虫,逆向)