某备案查询网站 汉字点选逆向分析
本帖最后由 li63033 于 2024-3-5 14:35 编辑## 声明
---
**本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!**
**本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!**
## 目标
---
目标:点选人机验证逆向分析
网址:`aHR0cHM6Ly9iZWlhbi5taWl0Lmdvdi5jbi8jL0ludGVncmF0ZWQvaW5kZXg=`
## 流程分析
---
进入页面前,先打开F12开发者工具
1. 上来就是个521大礼包,朋友们这可太熟悉了,妥妥的jsl,现在这东西逆向真的已经烂大街了(不排除有刚入坑的小伙伴,后期单独补上)
2. 未进行查询验证前,Network中我们会看到一个`/auth`接口进行认证操作,请求传递了两个参数。
- authKey:时间戳加盐后的MD5,需要逆向得到
- timeStamp:当前时间戳
接口返回了后续请求中,header需要携带的token
3. 点击查询验证后,出现了我们今天分析的主角:文字点选验证码,加载接口为`/getCheckImagePoint`,传递了一个参数
- clientUid:设备id,需要逆向得到
接口返回了点选验证码的详细信息
- bigImage:点选验证码上方的大图base64
- secretKey:加密坐标时的密钥
- smallImage:点选验证码下方的小图base64
- uuid:此验证码的id
- wordCount:需要点选的文字数量
4. 点选完毕后,进行验证的接口为`/checkImage`,传递了相关点选的数据
- clientUid:3中生成的clientUid
- pointJson:点击坐标密文,需要逆向得到
- secretKey:加密密钥
- token:加载接口返回的验证码id
未通过验证,会刷新验证码,验证返回的数据如下
通过验证,会跳转查询请求,验证返回数据中会多一个`sign`字段
5. 查询请求接口为`/queryByCondition`,需要再请求头中携带四个正确的字段,才能够请求成功
- Cookie:jsl生成的cookie,需要逆向得到
- Sign:点选验证码通过后,返回的sign
- Token:第一次进入系统,`auth`接口的返回值
- Uuid:点选验证码加载时的uuid
参数正确,返回的请求数据如下
## 逆向分析
---
### 1. `authKey`参数
跟到这个参数很简单,直接全局搜就能看到了
### 2. `clientUid`参数
这个参数也很简单,还是全局搜,我们会发现是已经生成好存在了localstorage中
所以我们干脆直接改为全局搜`localStorage.getItem`,就能定位到生成&存储的位置了
### 3. `pointJson`参数
仍然是全局搜,很快就能定位到参数生成的位置,入口为`h`函数,跟进去我们就能看到,是个AES加密,密钥为接口返回的`secretKey`,加密模式为`ECB`,填充模式为`Pkcs7`
### 4. Cookie中的`__jsluid_s`
这是一个辨识度很高的jsl防护,先埋个坑,后期单独出一期jsl的逆向
## 点选识别
---
其他所有流程步骤都分析完毕了,就差点选坐标的获取了。
啰嗦两句,其实现在针对点选方式验证码的解决,比较成熟的方案为机器学习+识别推理,模型训练的方法也比较集中为目标检测+孪生神经网络。目标检测经过长时间的发展改进,现在已经挺成熟了,所以难点不在找字,而在于找对点击顺序。ok,现在方向有了,开干
### 一、目标检测训练
目的:
- 识别大图中所有的文字位置
- 识别小图中需要点击的文字位置
步骤
1. **采集点选验证码的大图和小图**
这一步只要拿到了`Token`值,直接请求接口`getCheckImagePoint`就能拿到了
2. **分别对小图和大图中出现的目标字进行标注**
这一步中,我使用的标注工具为[`labelImg`](https://github.com/HumanSignal/labelImg),`labelImg`是一款非常优秀的图片数据标定工具,借助它我们能轻松完成数据集的标注。
点击 `Open Dir` ,选择验证码存放的路径。点击 `Change Save Dir`,选择标注结果存放的路径。点击`Pascal VOC`,它会变成`YOLO`,保存的结果可以直接拿来用。勾选右侧`Use default labtel`,并在输入框填入`text`,这样接下来所有标注的文字都会打上`text`的标签
按 `W` 键创建一个标记框,框起来验证区域的字。标注完一张图片后,记得按 `Ctrl` + `S` 保存。按 `D` 进入下一张图片,按 `A` 进入前一张图片。
> 我们在目标检测阶段只区分是不是字,不区分具体是什么字,所以标签只有一个 `text`。
>
> 下方点击顺序的四个字位置是固定的,所以后期我们统一进行自动化标注。
3. **使用YOLOv8训练目标检测模型**
使用YOLOv8前,确保你的电脑有N卡,并[安装](https://blog.csdn.net/qq_50677040/article/details/132131346)好了`CUDA`+`cudnn`相关深度学习的环境,安装完上述环境,再安装Python包`ultralytics`即可。
目标检测模型训练配置yaml
```yaml
# 指定训练集、验证集、测试集路径
train: /path/to/your/train/images
val: /path/to/your/valid/images
test: /path/to/your/test/images
# 有多少个分类
nc: 1
# 分类分别是什么
names: ['text']
```
目标检测模型训练代码
```python
from ultralytics import YOLO
# 预训练模型下载 https://github.com/ultralytics/ultralytics?tab=readme-ov-file#models
model = YOLO("./model/yolov8n.pt", task="detect")
model.train(data="./dataset/detect.yaml", epochs=20, cache=True, imgsz=320, batch=16, workers=0, device=0)
```
导出onnx模型
```python
model = YOLO('./path/to/your/train/output/best.pt', task='detect')
model.export(format='onnx', imgsz=320, simplify=True)
```
模型使用
```python
model = YOLO(model='./path/to/your/model/best.onnx', task='detect')
results = model.predict(source='./path/to/your/test/dataset/images/folder', show=False, save=True, imgsz=500, device=0)
print(f"total_标签名字:{results.names}")
def y8_detect_xy(result):
"""
输出坐标信息
:param result:
:return:
"""
cls_xy = list()
cls_dict = result.names
cls_all = result.boxes.cls.tolist()
print(f">>识别结果目标类 {len(cls_all)}个: {cls_all}")
xyxy_all = result.boxes.xyxy.tolist()
for i in range(len(cls_all)):
label_name = cls_dict)]
box_xyxy = xyxy_all
box_mid_xy = [(box_xyxy + box_xyxy) / 2, (box_xyxy + box_xyxy) / 2]
# print(f"目标点{i}: 标签名字: {label_name}, 中心坐标:{box_mid_xy}, xyxy坐标:{box_xyxy}")
cls_xy.append({
"label_id": i, "label_name": label_name,
"box_mid_xy": box_mid_xy, "xyxy": box_xyxy
})
print(f"==识别结果: {cls_xy}==")
return cls_xy
for result in results:
y8_detect_xy(result)
```
训练效果
### 二、孪生神经网络训练
目的:
- 判断待点击字在大图中出现的位置
- 在大图中按顺序找出小图中的文字
步骤:
1. **进行目标检测,裁剪预测的结果小图**
剪裁小图代码
```python
def extract_correct_word():
save_to = '/path/to/save/word'
if not os.path.exists(save_to):
os.mkdir(save_to)
images = glob('./cut/images/*.png')
for image_path in tqdm(images):
results = model.predict(source=image_path, show=False, save=True, imgsz=500, device=0)
num = image_path.split('/')[-1].split('.').split('_')
im = np.asarray(Image.open(image_path).convert("RGB"))
for result in results:
labels = y8_detect_xy(result)
for i, label in enumerate(tqdm(labels)):
x1, y1, x2, y2 = label["xyxy"]
word_im = im
cv2.imwrite(os.path.join(save_to, 'x-{}-{}.png'.format(num, i)), word_im)
```
人工标注后整理,效果如下
2. **进行孪生神经网络训练**
训练代码
预测结果
导出onnx模型
```python
def pth2onnx():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model_path = r"/path/to/your/train/output/model.pth"
model = Siamese((105, 105, 3))
model.load_state_dict(torch.load(model_path, map_location=device))
model.eval()
dummy_input =
torch.onnx.export(
model, dummy_input, 'weights_self_12071503.onnx', verbose=True, input_names=['x1', 'x2'], output_names=['output']
)
print("Successful!")
```
### 三、模型融合使用
目的:
- 在验证点击区,依次找出待点击的文字
核心代码:
```python
def judge_max_sim(self, cls_xys, img):
targets = [(i["xyxy"], i["box_mid_xy"]) for i in cls_xys if i["label_name"] == "target"]
target_chars = []
# 将提供的顺序,按照x坐标轴从小到大排序,确保识别的字符顺序正确
targets = sorted(targets, key=lambda x: x)
for xyxy, _ in targets:
target_chars.append(img.crop(xyxy))
texts = [(i["xyxy"], i["box_mid_xy"]) for i in cls_xys if i["label_name"] == "text"]
text_chars = []
for xyxy, _ in texts:
text_chars.append(img.crop(xyxy))
# 获取点击顺序
click_seq_result = []
for m, target_img in enumerate(target_chars):
slys = []
if len(texts) == 0:
break
elif len(texts) == 1:
slys_index = 0
else:
for n, text_img in enumerate(text_chars):
similarity = self.siam_model.predict(target_img, text_img)
slys.append(similarity)
slys_index = slys.index(max(slys))
click_seq_result.append(texts)
texts.pop(slys_index)
text_chars.pop(slys_index)
if len(texts) == 0:
break
return click_seq_result
def get_xy_seq(self, img_path):
"""
获取点击顺序
:param img_path:
:return:
"""
img = self.open_image(img_path)
# yolov8识别
obj_result = self.yolo_v8_model_predict(img_path)
cls_xys = self.target_detection_xy(obj_result)
# 孪生判断相似度
xy_seq = self.judge_max_sim(cls_xys, img)
return xy_seq
```
## 结果验证
---
前排留名。
备案查询接口倒是容易找到,如果是部分行业,还能简单的正规取得官方接口对接(早几年在取得资质的时候就有要求对接联调)。
本文的要点是过验证。
我没用机器学习。类似的验证码用的是色彩。先判断背景色,再寻找在多大的范围内出现了连续的单色,然后对比得到位置。
相对来说还是机器学习最终更简单粗暴。
就类似动态识别号码,物品动态识别,最终都是落入机器学习模糊计算的流程了。。人工算法取巧既复杂,又容易受到干扰。机器学习在有一定数据量的情况下,识别验证码怕是比人工强更多 厉害厉害,学习学习 如果能拿到对应服务器大部分的验证图片,训练结果应该也是可靠的,不知道接口有没有验证码请求限制 好家伙,都用上yolo来识别了 哈哈哈感谢楼主分享啊啊啊{:1_893:} 高级啊,能把源码发出来不 厉害厉害,学习学习 学到了,感谢分享!!!{:1_921:} 高手啊~~学习了,没准哪天就用到了呢~~