每月看好文
一、前言介绍
大家有没有想过,每次在网上注册账号或者登录时,那些不使用滑块验证码,也不依赖HTTP协议的验证码是如何识别我们是人而不是机器的呢?这些验证码可能只需要你简单地进行识别操作,例如输入一些文字或数字。但你可曾想过,这些看似简单的验证背后,蕴含着怎样的科技奥秘?在本文中,我们将揭开非HTTP验证码的神秘面纱,一起探索它的工作原理,了解它是如何巧妙地识别真人身份的。在这个有趣的旅程中,我们将一窥技术背后的精妙,带你进入一个充满挑战和创新的数字世界。快来和我一起探索吧!
二、网站分析
1、从某APP中抓包分析用户登录接口,发现登录接口为webview,将登录接口url使用浏览器打开分析,截图如下:
2、打开浏览器开发者工具,然后输入手机号后,点击发送验证码,请求截图如下所示:
3、浏览器Network中查看请求包,我们并没有发现http请求接口的验证码,只发现了疑似验证码的请求体和响应体,截图如下:
总结:此处我们发现response的返回内容进行了加密,加密参数为encrypt_body。我们再分析下request请求体,结果发现加密参数也是encrypt_body。这里我们初步可以确定,请求入参和response出参使用的是一套加密算法,接下来我们进入逆向分析环节吧。
三、逆向分析
1、通过Initiator进行调试分析,过滤无限debugger模式,进行断点分析截图如下所示:
2、这里我们定位到了请求体加密前参数,先暂时忽略。我们紧接着往下继续分析,截图如下:
3、此处我们查看返回参数s,可以确定是请求体加密后的参数,截图如下:
总结:虽然前面简单分析过,请求体和响应体的encrypt_body使用的是一套加密算法。那么我们先还原请求体加密,再拿请求体加密的对称加密算法进行解密,就可以判断我们前面的猜测是否正确。通过断点分析我们发现请求体加密使用了aes-ecb模式,AES是一种对称加密算法,这意味着加密和解密使用相同的密钥。接下来我们进入算法还原环节,来验证下我们的猜测吧。
四、算法还原
1、使用Python还原JS加密算法,还原后的完整加密算法如下:
# -*- coding: utf-8 -*-
# -------------------------------
# @author : TheWeiJUn
# @time : 2024.08.04 20:58:33
# -------------------------------
from
Crypto.Cipher
import
AES
import
json
import
base64
from
Crypto.Util.Padding
import
pad
def
encrypt
(e, n)
:
if
e
and
n:
if
not
isinstance(e, str):
e = json.dumps(e, separators=(
","
,
":"
))
r =
'utf-8'
key = n.encode(r)
# Assuming n is a string that needs to be encoded to bytes
data = e.encode(r)
cipher = AES.new(key, AES.MODE_ECB)
padded_data = pad(data, AES.block_size)
# Pad the data to be a multiple of block size
cipher_text = cipher.encrypt(padded_data)
# Encrypt the padded data
s = base64.b64encode(cipher_text).decode(
'utf-8'
)
return
s
if
__name__ ==
'__main__'
:
# Example usage:
e = {
"phone"
:
"xxxx"
,
"countryCode"
:
"CN"
,
"type"
:
"login"
}
n =
"1234567890123456"
# Example key (must be 16 bytes for AES-128)
result = encrypt(e, n)
print(result)
2、输出Python加密后的参数s同浏览器中生成的加密参数s进行比对,截图如下:
总结:经过对比我们发现,两个参数字符串完全一致。接下来我们使用前面分析的方法对response加密体参数encrypt_body试着进行对称解密还原。截图如下所示:
3、解密还原失败,此处我怀疑response加密参数encrypt_body应该是做了特殊处理,经过断点分析后,果真如我所言,编辑修改后的decrypt解密代码如下:
# -*- coding: utf-8 -*-
# -------------------------------
# @author : TheWeiJUn
# @time : 2024.08.04 20:58:33
# -------------------------------
from
Crypto.Cipher
import
AES
import
json
import
base64
from
Crypto.Util.Padding
import
pad, unpad
def
encrypt
(e, n)
:
if
e
and
n:
if
not
isinstance(e, str):
e = json.dumps(e, separators=(
","
,
":"
))
r =
'utf-8'
key = n.encode(r)
# Assuming n is a string that needs to be encoded to bytes
data = e.encode(r)
cipher = AES.new(key, AES.MODE_ECB)
padded_data = pad(data, AES.block_size)
# Pad the data to be a multiple of block size
cipher_text = cipher.encrypt(padded_data)
# Encrypt the padded data
s = base64.b64encode(cipher_text).decode(
'utf-8'
)
return
s
def
decode_base64
(t)
:
# Add padding to make the length a multiple of 4
t = t +
'='
* (
4
- len(t) %
4
)
# Replace URL-safe characters with standard Base64 characters
t = t.replace(
'-'
,
'+'
).replace(
'_'
,
'/'
)
# Decode the Base64 string
return
base64.b64decode(t)
def
decrypt
(e, n)
:
if
e
and
n:
# Decode the Base64 encoded input
cipher_text_bytes = decode_base64(e)
# Define the encoding and decoding formats
a =
'utf-8'
key = n.encode(a)
# Convert key to bytes
# Create the cipher object for decryption
cipher = AES.new(key, AES.MODE_ECB)
padded_data = cipher.decrypt(cipher_text_bytes)
# Decrypt the data
# Remove padding
data = unpad(padded_data, AES.block_size)
# Parse the JSON data
return
json.loads(data.decode(a))
if
__name__ ==
'__main__'
:
n =
"1234567890123456"
# Example key (must be 16 bytes for AES-128)
e =
"encrypt_body"
result = decrypt(e, n)
print(result)
4、对加密参数进行padding填充及对特殊符号进行替换处理后,即可进行对称解密操作,解码后的截图如下所示:
5、对还原后的响应体graph参数进行特殊处理后,进行base64decode,相关代码和解码后保存的字节流如下:
6、到这里基本就结束了,接下来进行验证码识别还原,这个相对来说比较简单,我们这里使用ddddocr,编辑代码后还原验证码如下所示:
五、文章结语
本篇分享到这里就结束了,感谢大家的阅读和支持。如果你对爬虫逆向分析、验证码破解及其他技术话题感兴趣,记得关注我的公众号,不错过下一期的更新。我们将继续深入探讨各种技术细节和实用技巧,一起探索数字世界的奥秘。期待与你在下期文章中再见,一起学习,一起进步!☀️☀️✌️
如果想要获得更多精彩内容可以关注我朋友:
点分享
点收藏
点点赞
点在看
原文始发于微信公众号(逆向与爬虫的故事):非HTTP验证码别乱捅!一不小心就反爬了。
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论