免责声明
文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业和非法用途,否则由此产生的一切后果与作者无关。若有侵权,请在公众号【爬虫逆向小林哥】联系作者
01
—
逆向目标
最近圈友高质量的文章一篇接一篇,倍感焦虑,告别内卷,拥抱金角
aHR0cHM6Ly9kb3VqaWEuZG91eWluLmNvbS9sb2dpbg==
02
—
抓包分析
某音投放平台的登录验证
03
—
逆向过程
过程没啥说的,难点就是底图还原跟locator定位(好久没写过selector了),真好啊 不用动脑子
通过率测了下是100%,过程不写了,直接放源码,需要的自提
记得改下面的地方
04
—
算法还原
import httpx
import random
import asyncio
import ddddocr
from PIL import Image
from io import BytesIO
from loguru import logger
from playwright.async_api import Page, Response, async_playwright
class AwemeBrowser:
def __init__(self, session: str, headless: bool = False):
self.x = 0
self.y = ""
self.url1 = ""
self.url2 = ""
self.tag = False
self._session = session
self._headless = headless
self.ocr = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
async def login(self, page: Page, username) -> None:
page.on("response", self.on_response)
await page.goto('aHR0cHM6Ly9kb3VqaWEuZG91eWluLmNvbS9sb2dpbg==')
await page.wait_for_timeout(self.wait_random)
if "扫码登录" not in await page.content():
logger.info("账号={}, 正常在线".format(username))
else:
await page.click('li:has-text("验证码登录")')
# 清空输入框
await page.evaluate('document.querySelector(".web-login-normal-input__input").value=""')
await page.evaluate('document.querySelector(".web-login-button-input__input").value=""')
# 账号密码登录
await page.wait_for_timeout(self.wait_random)
await page.type(selector=".web-login-normal-input__input", text=username, delay=100)
# await page.type(selector=".web-login-button-input__input", text=password, delay=100)
await page.wait_for_timeout(self.wait_random)
await page.click('span:has-text("获取验证码")')
await page.wait_for_timeout(self.wait_random)
num = 1
while True:
logger.warning("当前滑动次数: {}".format(num))
distance = await self.get_distance()
tracks = self.get_tracks(distance)
new_frame = page.frame_locator('#captcha_container > iframe')
slide = new_frame.locator('.captcha-slider-btn')
box = await slide.bounding_box()
await page.mouse.move(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
await page.mouse.down()
x = box["x"] + box["width"] / 2
for track in tracks:
x += track
await page.mouse.move(x, box["y"])
await asyncio.sleep(random.uniform(0.005, 0.02))
await page.mouse.up()
await page.wait_for_timeout(self.wait_random)
num = num + 1
if num > 5 or self.tag:
break
await page.wait_for_timeout(self.wait_random)
def wait_random(self):
return random.randint(1000, 3000)
async def on_response(self, response: Response):
if "https://verify.zijieapi.com/captcha/get" in response.url:
result = await response.json()
data = result.get("data")
picture = data.get("question")
self.y = picture.get("tip_y")
self.url1 = picture.get("url1")
self.url2 = picture.get("url2")
if "https://verify.zijieapi.com/captcha/verify" in response.url:
result = await response.json()
if result.get("code") == 200:
logger.success("滑块通过, res={}".format(result))
self.tag = True
async def get_distance(self):
async with httpx.AsyncClient() as session:
response_1 = await session.get(self.url1)
response_2 = await session.get(self.url2)
image = Image.open(BytesIO(response_1.content))
width, height = image.size
sub_images = []
for i in range(6):
left = (i * width) // 6
right = ((i + 1) * width) // 6
sub_image = image.crop((left, 0, right, height))
sub_images.append(sub_image)
order = [1, 5, 4, 2, 0, 3]
new_images = [None] * 6
for i in range(6):
new_images[order[i]] = sub_images[i]
merged_image = Image.new('RGB', (width, height))
for i in range(6):
left = (i * width) // 6
right = ((i + 1) * width) // 6
merged_image.paste(new_images[i], (left, 0))
# 保存图片
merged_image.save('logs/images/captcha_restored.png')
img_byte = BytesIO()
merged_image.save(img_byte, format='JPEG')
url1_content = img_byte.getvalue()
ocr_response = self.ocr.slide_match(response_2.content, url1_content)
x = ocr_response['target'][0]
logger.info(f"滑动的距离为: {x}, 高度为: {self.y}")
return int(int(x) / 550 * 340) - 5
def get_tracks(self, distance):
track = []
current = 0
mid = distance * 4 / 5
t = 0.2
v = 1
while current < distance:
if current < mid:
a = 4
else:
a = -3
v0 = v
v = v0 + a * t
move = v0 * t + 1 / 2 * a * t * t
current += move
track.append(round(move))
return track
async def start(self, username):
async with async_playwright() as pwt:
browser = await pwt.chromium.launch_persistent_context(
headless=self._headless,
user_data_dir=self._session,
args=["--start-maximized", '--disable-blink-features=AutomationControlled'],
no_viewport=True # 窗口最大化
)
await browser.add_init_script(path="logs/js/stealth.min.js")
page = await browser.new_page()
await self.login(page=page, username=username)
async def main(username):
b = AwemeBrowser(session=f"logs/session/{username}")
await b.start(username)
asyncio.run(main("18888888281"))
05
—
归纳总结
添加好友回复:交流群
原文始发于微信公众号(爬虫逆向小林哥):【自动化系列】plawright过某音登录验证
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论