猿人学js逆向第三届(验证码专题) · 第一题

Sherry 发布于 2025-06-10 93 次阅读 文章 最后更新于 2025-06-10 1053 字


🍔题目地址

aHR0cHM6Ly9tYXRjaDIwMjUueXVhbnJlbnh1ZS5jbi9tYXRjaDIwMjUvdG9waWMvMQ==

🌮请求

一个 gif


一个 a

响应也加密

检查表单

text 加密



我们发现这里有发送一个 XHR 请求的 logo 查看响应发现是一个时间戳 bases 64 加密


直接追启动器发现是 Zf 中 data 为 a 参数


在最前方挂上代理,之后将整个 js 拷过来,进行补环境

我们调试一下第一次遇到 window 未定义

浏览器测试为 true,我们直接将代码改成 true

第二次运行$不存在

我们直接使用 墨竹 大佬的 makeFunction 函数生成恒等于
$$
$ = function (){debugger}
$$

function makeFunction(name) {  
    // 动态创建一个函数  
    var func = new Function(`  
        return function ${name}() {  
            console_log('函数传参.${name}',arguments)  
        }    `)();  
    safeFunction(func)  
    func.prototype = watch(func.prototype, `方法原型:${name}.prototype`)  
    func = watch(func, `方法本身:${name}`)  
    return func;  
};


再次调试

定位过来

我们挂上代理后,发现一个 window 调用都没有,在开始和断点处打印 window,发现一开始是挂上代理的,后面 window 被覆写了

这里看到 window 被覆写掉了,我们直接返回空对象就行
继续跟 window 看看还有没有其他地方检测


浏览器可以看到值为 window

比对为 false
我们直接将代码修改为
$$
Lr = false
$$
window 暂时结束,接下来对 document 进行查看

同样覆写,我们将其改为返回空对象
document 结束,对 navigator 进行查看

一样返回空对象,结束,接下来看 location,location 没有
localStorge 也没有
screen 也没有


暂时检测点都没了,开始运行补环境

navigator 有个 cookie

直接 true

写成 true(这里可以调试确认一下)

到发包这里,她这个 ajax 也是魔改的

一样补成方法,可以看到断到这里了

这时候可以看到 arguments 变为我们需要的数据

直接将 data 导出,整体包裹成一个函数


可以看到成功导出参数 a

我们尝试 python 调用一下

也是成功调用出来了

但是别忘记我们刚刚观察请求发现 api 请求前还请求了一个 logo 的时间戳

我们跟进 date 里面

我们打印可以看到 date 是被魔改过的

可以看到这个结果是有影响的,经过测试如果将logo 请求禁用是获取不到验证码的
重构脚本,先请求 logo,之后 logo 返回值作为参数,生成 a 参数


成功拿到响应

🍕检查


我们下一个 XHR 断点断住

走的还是
$$
$.ajax
$$
但是这次 data 变成了 text

可以看到输入调用了
$$
text_oninput(this)
$$
我们编写油猴脚本进行 hook 定位

// ==UserScript==
// @name         text_oninput
// @namespace    http://tampermonkey.net/
// @version      2025-06-10
// @description  try to take over the world!
// @author       Sherry
// @match        https://*/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    Object.defineProperty(window,'text_oninput',{
        set:function(){
            debugger
        }
    })
})();


最终调用了一个 Kf 函数

传了两个参数,一个 Object,一个 Arrary


执行到最后就自己设置了一个函数 t

定位位置通过 window 给 kf 导出

直接调用 apply

拿到两个参数

t 值复制过来挂上代理

运行找不到 location


直接将浏览器的拿过来替换了


又报错了, 提示找不到 document. location, 但是
$$
document.location=location
$$

一步到位,补齐

说找不到 attr 参数

重新补一下方法


是个空,那就不用管了

又报错了

构成函数

成功设置上

上下滚动发现就利用 t[1]和 t[11],其他可以设置为空对象

最终 src 为图片

我们直接将其导出


我们传入 result 进行调用

def download_gif():  
    with open('./a.js', 'r', encoding='utf-8') as f:  
        jscode = f.read()  
    jsexec = execjs.compile(jscode)  
    result,timestamp = send_png()  
    base64_data = jsexec.call('get_data', timestamp, result)  
    base64_data = base64_data.replace('data:image/gif;base64,', '')  
    gif_data = base64.b64decode(base64_data)  
    with open("output.gif", "wb") as gif_file:  
        gif_file.write(gif_data)  
    print("GIF 文件已保存为 output.gif")


成功保存

识别之后就差发包检查了,刚刚分析判断是调用了 text_oninput 方法,我们手动调用一下

传入 code

得到 text