乐某自研滑块逆向分析

查看 20|回复 2
作者:buluo533   
接单遇到一个网站,发现登陆有个中等难度的滑块,话不多说。直接干:
[color=]aHR0cHM6Ly93d3cubG9mdGVyLmNvbS9mcm9udC9sb2dpbi8=
                                                      


image.png (124.52 KB, 下载次数: 0)
下载附件
滑块
2025-6-20 17:04 上传

食用前提:
(1)python基础(python复现代码)
(2)js基础(看懂js加密逻辑)
(3)密码学基础(涉及sha256,aes,rsa,base64)
密码学学习文章:
密码学常用基本数据类型和运算在不同语言当中的体现------aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvdFg2V3NFdHNic2FvOUtiUlFwZXJIQQ==
sha256:一文读懂SHA-2-------aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvUG5ScHJNc01vM3VxUG5JQ25KQlUzZw==
base64:
一文读懂Base64-------aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvSVpjOVJIakxSZmJzQzhibDNUR3FIUQ==
aes:
分析crypto-js当中AES的实现----aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvRlBjUzlvRUQ5UHFDOV9nLXpmTEhqUQ==
rsa:
加密的诗篇:RSA与Padding技术的艺术融合------aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvaU0xS1lldy13R1VORXBwWk1YTG5vdw==RSA当中整数的表示方法 ------aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvMWhQN090RUNOV2VqN2Vpd3RReExMQQ==
                        
经历过一次滑块(如果没有可以看我之前的文章),我们简单先整理一下处理滑块的思路
(1)一般的滑块最开始肯定要先加载图片的的地址,拿到图片信息用于后续的距离或者轨迹,同时可能包含后续会用的加密参数
(2)分析滑块的加密逻辑,根据上一次的经验,加密参数请求会返回值用于登录
(3) 整理返回的加密信息,完成登录
一、流程分析
        我们直接输入好账号密码,可以看到返回两个接口信息,第一个是登录的验证,第二个就是返回的图片信息(大胆假设,小心求证),我们先看一下,因为没有下断点,图片信息肯定也加载出来了,对比一下
            


image.png (50.54 KB, 下载次数: 0)
下载附件
两个接口
2025-6-20 17:07 上传

           
        大致看一下,大概是一个是底图,一个是滑块的图片,有一个id,一个type(大概就是类似版本号的东西),
         


image.png (45.28 KB, 下载次数: 0)
下载附件
图片接口
2025-6-20 17:08 上传

               
               
       很明显发现了端倪,返回值确实是图片地址,可以看出协议的信息
[color=]data:image/png;base64,
  说明可以通过base64编码读取图片信息,从而保存底图和滑块,id可以暂时留着,没看出来用处。
      这个的滑块和之前的有点差别,点击的的登录后加载的。
            
        


image.png (39.17 KB, 下载次数: 0)
下载附件
密码加密
2025-6-20 17:10 上传

     可以看到我们的密码被加密了一次,同时呢,返回值是提示要求验证码进行校验,可以猜一下,大概是通过登录拿到cookie再去请求图片信息,这样再去通过滑块的验证,保险起见,可以测试,所以我们要先处理密码的加密方式
     接下来我们滑动一下看看情况.
      
            我们滑动测试一下发现,有一个校验的接口,接下来因为错误就重新加载了图片信息,看一下校验接口是怎么校验的信息。
                     
                    


image.png (27.6 KB, 下载次数: 0)
下载附件
滑动接口
2025-6-20 17:18 上传

                    


image.png (32.45 KB, 下载次数: 0)
下载附件
请求头加密
2025-6-20 17:18 上传

               
                 我勒个豆,还是挺多的三个加密点要处理,应该就是直接从这里返回值看看有没有成功,但是都是加密后的结果
                  


image.png (23.39 KB, 下载次数: 0)
下载附件
返回值加密
2025-6-20 17:19 上传

二、加密定位与还原
               1、登录接口,密码加密
     对密码的调用栈直接下断点,重新输入账号密码跟栈(上一篇文章有详细的跟栈思路)
            


image.png (74.8 KB, 下载次数: 0)
下载附件
断点1
2025-6-20 17:20 上传

            


image.png (117.22 KB, 下载次数: 0)
下载附件
密码加密
2025-6-20 17:22 上传

           到这里我们可以明显地看到密码的加密逻辑,我们直接用标准的来进行测试然后用python还原
         


image.png (28.75 KB, 下载次数: 0)
下载附件
sha256
2025-6-20 17:24 上传

        还是很明显的一致,接下来用python还原登录
       [Python] 纯文本查看 复制代码from hashlib import sha256
def sha256_password(text):
    return sha256(text.encode('utf-8')).hexdigest()
              
         接下来实现登录的逻辑
[Python] 纯文本查看 复制代码import time
def log_in(pass_word):
    time_temp = int(time.time() * 1000)
    passport = sha256_password(pass_word)
    cookies = {
    }
    headers = {
    }
    params = {
        'product': 'lofter-pc',
        '_': time_temp,
    }
    data = {
        'phone': '脱敏处理',
        'passport': passport,
        'type': '0',
        'clientType': '0',
        'deviceType': '3',
        'Target': 'www.lofter.com',
    }
    response = session.post('脱敏处理', params=params, cookies=cookies, headers=headers,
                            data=data).json()
   2、图片请求地址与距离识别
    [Python] 纯文本查看 复制代码def get_pic():
    cookies = {}
    headers = {}
    params = {
        'type': 'jigsaw',
    }
    response = session.get('脱敏处理', params=params, cookies=cookies,
                           headers=headers).json()
    pic_id = response['data']['id']
    pic_gap = response['data']['front']
    pic_bg = response['data']['bg']
    gap_pic = base64.b64decode(pic_gap)
    bg_pic = base64.b64decode(pic_bg)
    with open('gap.jpg', 'wb') as f:
        f.write(gap_pic)
    with open('bg.jpg', 'wb') as f:
        f.write(bg_pic)
    det = ddddocr.DdddOcr(det=False, ocr=True, show_ad=False)
    with open('gap.jpg', 'rb') as f:
        target_bytes = f.read()
    with open('bg.jpg', 'rb') as f:
        background_bytes = f.read()
    res = det.slide_match(target_bytes, background_bytes, simple_target=True)
    value = res['target'][0]
    print('识别到距离', value)
    return pic_id, value
    这一部分逻辑就很简单了,主要就是要理解他是怎么产生的,看懂协议,用base64编码后直接读取保存下来
   


image.png (105.06 KB, 下载次数: 0)
下载附件
底图
2025-6-20 17:32 上传

     


image.png (12.37 KB, 下载次数: 0)
下载附件
滑块
2025-6-20 17:33 上传

  提取出图片之后直接用ddddocr识别到距离。
    3、滑块加密逻辑还原
           还是一样的
      


image.png (345.18 KB, 下载次数: 0)
下载附件
滑块断点
2025-6-20 18:10 上传

         


image.png (42.9 KB, 下载次数: 0)
下载附件
请求体加密算法
2025-6-20 19:30 上传

      


image.png (173.72 KB, 下载次数: 0)
下载附件
加密栈
2025-6-20 19:32 上传

    这个跟栈也是非常的简单,核心是他的加密算法逻辑,命名非常的直接,我们明显看出的是aes和base64,这就要大佬们读读密码学的文章,方便后续的分析
     


image.png (22.77 KB, 下载次数: 0)
下载附件
aes加密
2025-6-20 19:33 上传

     直接进到aes里面,先看一下传入的参数有哪些,很明显,我们之前请求剩下的id有用了,然后就是一个距离,我们通过ddddocr已经搞定了,这样的话我们就知道这个接口的请求体就是一个图片接口返回的id和一个距离
     


image.png (31.3 KB, 下载次数: 0)
下载附件
请求体加密参数
2025-6-20 19:42 上传

    同时我们可以看到
[color=]iv,padding,key
这些非常关键的参数,但是这个又有些不同寻常,我们经常看到的加密一般是这样的写的(举个例子)
   [JavaScript] 纯文本查看 复制代码let key = crypto.enc.Utf8.parse('key');
let iv = crypto.enc.Utf8.parse('iv' );
function aesEncrypt(plaintext) {
    let encrypted = crypto.AES.encrypt(
        crypto.enc.Utf8.parse(plaintext),
        key,
        {
            iv: iv,
            mode: crypto.mode.CBC,
            padding: crypto.pad.Pkcs7
        }
    );
    return encrypted.toString();
}
   但是这次很明显有一些不同,我们继续跟进encrprt的逻辑中进行分析


image.png (19.2 KB, 下载次数: 0)
下载附件
aes2
2025-6-20 19:37 上传


  继续进去
           


image.png (32.33 KB, 下载次数: 0)
下载附件
aes3
2025-6-20 19:37 上传

    这里我们能看到大概的逻辑了,就是一个非常明确的aes加密的逻辑,这里是一些初始化的操作。然后回到梦开始的地方,因为现在函数里面的内容都是在最开始传进来的
   


image.png (56.68 KB, 下载次数: 0)
下载附件
aes4
2025-6-20 19:48 上传

    其实我们稍微往上看一点就看到了key和iv随机生成的一个位置,随机生成的代码也清晰可见了
[JavaScript] 纯文本查看 复制代码getRandomString = function(t) {
                for (var e = "", n = 0; n
    我们直接python还原一手,暂时没看到其他参数值的赋值,先写成16位
[Python] 纯文本查看 复制代码
import random
def getRandomString(t=16):
    s = ""
    for _ in range(t):
        if random.randint(0, 1):
            s += str(int(9 * random.random()))
        else:
            s += chr(int(25 * random.random()) + 97)
    return s
接下来就是看看padding和加密模式,常见的有iv的就是CBC(不懂的还是建议补一补密码学基础),我们直接猜一下,也可以翻翻源码,也有惊喜的发现(我刚开始确实不是很熟悉这个,所以也是这样找到的,半猜半蒙)
  


image.png (35.52 KB, 下载次数: 0)
下载附件
padding
2025-6-20 19:55 上传

   这样有了padding和加密模式,这样我们先用js还原一下测试一下,再用python来复现
            


image.png (11.58 KB, 下载次数: 0)
下载附件
加密参数1
2025-6-20 19:58 上传

        


image.png (40.23 KB, 下载次数: 0)
下载附件
aes加密结果
2025-6-20 19:59 上传

      


image.png (84.35 KB, 下载次数: 0)
下载附件
结果对比
2025-6-20 20:00 上传

     非常完美!!!一模一样,接下是python的还原,众所周知,crypto.enc.Utf8.parse(iv)在python中等价于iv.encode('utf-8')(不懂的还是密码学基础)
     这样我们继续还原
     [Python] 纯文本查看 复制代码from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesfrom cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.padding import PKCS7
def aesEncrypt(plaintext, aes_iv, aes_key):
    if isinstance(aes_iv, str):
        aes_iv = aes_iv.encode('utf-8')
    if isinstance(aes_key, str):
        aes_key = aes_key.encode('utf-8')
    cipher = Cipher(algorithms.AES(aes_key), modes.CBC(aes_iv), backend=default_backend())
    encryptor = cipher.encryptor()
    padder = padding.PKCS7(128).padder()
    padded_data = padder.update(plaintext.encode('utf-8')) + padder.finalize()
    ciphertext = encryptor.update(padded_data) + encryptor.finalize()
    return base64.b64encode(ciphertext).decode('utf-8')
     试试加密结果对照
     


image.png (358.13 KB, 下载次数: 0)
下载附件
python结果
2025-6-20 20:06 上传

    非常ok的搞定了,接下来就是base64编码的操作了
      


image.png (4.78 KB, 下载次数: 0)
下载附件
base64
2025-6-20 21:22 上传

      


image.png (22.06 KB, 下载次数: 0)
下载附件
编码操作
2025-6-20 21:22 上传

   至于怎么看出来的一个是js中的atob和btoa,以及密码学常用基本数据类型和运算在不同语言当中的体现(q佬文章),下一个就是对请求头参数的处理
4、请求头加密参数处理
     


image.png (17.23 KB, 下载次数: 0)
下载附件
请求头参数
2025-6-20 21:24 上传

    还是老规矩,跟进去看看
   


image.png (18.14 KB, 下载次数: 0)
下载附件
rsa
2025-6-20 21:25 上传

   这个还是很明显,一个公钥,加密内容是aes的key拼接一个-和iv,接下可以简单看一下rsa算法(求助过q佬,rsa的魔改点很少),可以简单结合文章和源码了解一下rsa
        


f4193b2f1f9d641fc402c4d559a3ddb.png (101.55 KB, 下载次数: 0)
下载附件
rsa算法
2025-6-20 21:28 上传


        


d3ce244a24abaa22175a614f8cdbbf4.png (37.31 KB, 下载次数: 0)
下载附件
大数组
2025-6-20 21:28 上传

    很标准的rsa算法,大数组,我们找一下公钥,然后用python还原
   


image.png (227.55 KB, 下载次数: 0)
下载附件
公钥存储
2025-6-20 21:30 上传

   


image.png (29.98 KB, 下载次数: 0)
下载附件
公钥
2025-6-20 21:29 上传

  经常加密的大佬应该知道rsa以pem的格式存储公钥
   [Python] 纯文本查看 复制代码from Crypto.PublicKey import RSAfrom Crypto.Cipher import  PKCS1_v1_5
def x_encseckey_get(rsa_iv, rsa_key):
    with open('public.pem', 'r') as f:
        public_key = f.read()
    datas = key + "-" + iv
    text = datas.encode('utf-8')
    pk = RSA.importKey(public_key)
    cipher = PKCS1_v1_5.new(pk)
    cipher_text = cipher.encrypt(text)
    cipher_text_base64 = base64.b64encode(cipher_text).decode('utf-8')
    return cipher_text_base64


image.png (160.91 KB, 下载次数: 0)
下载附件
rsa生成
2025-6-20 21:35 上传

   我们的生成是没有问题的,这个可能不好检测,但是可以通过生成公私钥对来测试加密情况,方便检验rsa的效果
  5.返回值参数解密算法分析
   


image.png (49.24 KB, 下载次数: 0)
下载附件
返回值
2025-6-20 21:37 上传

    我们可以看到,在对接口请求之后,就开始处理返回值的信息了,熟悉js的伙伴都知道,js请求都是异步操作,这也加大了还原的难度还有一个默认的arrayBuffer()函数,我们可以去js官方文档查查成分
   官方网址:JavaScript 基础 - 学习 Web 开发 | MDN
   


image.png (136.07 KB, 下载次数: 0)
下载附件
js函数解释
2025-6-20 21:39 上传

    也就是将返回值转化为二进制
   


image.png (24.51 KB, 下载次数: 0)
下载附件
断点放开
2025-6-20 21:42 上传

   将断点到异步执行后后查看t的值
   


image.png (26.5 KB, 下载次数: 0)
下载附件
t的
2025-6-20 21:44 上传

    这部分对应python的实现还是可以参考文章内容
    [Python] 纯文本查看 复制代码def array_buffer_to_base64(buffer):
    if isinstance(buffer, list):
        data = bytes(buffer)
    elif isinstance(buffer, bytearray):
        data = bytes(buffer)
    else:
        # 直接使用 bytes 对象
        data = buffer.encode('utf-8')
    return base64.b64encode(data).decode('utf-8')
def base64_to_array_buffer(base64_str):
    decoded_data = base64.b64decode(base64_str)
    return decoded_data
    接下来就是跟进去对t的一个处理逻辑
   


image.png (20.14 KB, 下载次数: 0)
下载附件
t的处理
2025-6-20 21:47 上传

   嘎嘎熟悉的一个base64操作,btoa
    接下来就是通过aes进行的一个解密操作
   


image.png (22.54 KB, 下载次数: 0)
下载附件
aes
2025-6-20 21:49 上传

   这里需要主义的就是n.parse和之前的s.parse不一样,我们可以跟进去看一下
   


image.png (46.19 KB, 下载次数: 0)
下载附件
t.parse
2025-6-20 21:50 上传

   还是一个base64的运算操作,然后就是很常规的解密操作,key和iv都是之前生成的,以及s.parse的操作分析,接下里是一个base64decoed的函数操作,依旧跟进去看看
   


image.png (17.85 KB, 下载次数: 0)
下载附件
basedeoced
2025-6-20 21:54 上传

  
  


image.png (91.71 KB, 下载次数: 0)
下载附件
w函数
2025-6-20 21:56 上传

   我们可以看到w函数的位置,以及整个的调用逻辑(不清楚w是函数的可以看看js基础语法,箭头函数),这个能看出来或者借助ai工具能分析出来是一个base64的操作
   


image.png (9.62 KB, 下载次数: 0)
下载附件
结果
2025-6-20 21:58 上传

  我们可以看到最后结果应该是这样子的,整体来说返回值是一个base64+aes解密过程,之前代码和思路很清晰了,大佬们可以尝试一下还原,评论区晒晒成果
三、总结
这段时间狠狠地研究和回顾了一下学习的过程,刚开始看到这个网站的时候以为很简单,后面越做越难受,发现还是对密码学理解太浅,在拷打ai和q佬的指导下也算是搞定了,狠狠地补了一下密码学。这一个滑块总的来说还是很有研究的价值,很多js的语法特性与密码学的结合,以及不同语言之前的特性糅合,适合进阶的小伙伴食用。我也可以安心去准备期末考试了,我还要肝下一篇ast

下载次数, 下载附件

ragkan   

NB,看着很强哟~~~
Yifan2007   

你这js逆向成本太大了,这么正的图直接图色解决不省时间吗
您需要登录后才可以回帖 登录 | 立即注册

返回顶部