【Web】记一次破解 JS 加密

一直在寻找一个可用的搜题接口

之前刷网课搜题一直用的 “帅搜”,写了个 py 的脚本,平常用起来很方便。
后来也不知道什么原因,人家把接口给关闭了。

import win32clipboard as win
import win32con
from requests_html import HTMLSession,sys
from urllib import parse
import time

def getText():
    win.OpenClipboard()
    text = win.GetClipboardData(win32con.CF_TEXT)
    win.CloseClipboard()
    return text

last = ''
while True:
    try:
        temp = str(getText(), 'gbk')
        pre = temp
        if last != pre:
            print('问题:' + pre)
            html = HTMLSession().get('http://chati.xuanxiu365.com/index.php?q=' + parse.quote(pre)).html
            print('\n' + '未查询到您需要的答案...' + '\n' + '\n' if '未查询到' in html.html or '禁止' in html.html else '\n' + html.find('body > form > div.weui-panel > div.weui-panel__bd > div > div', first=True).text + '\n' + '\n')
            last = pre
    except:
        last = ''
    time.sleep(1)

后来就只能去微信公众号上搜题,但总感觉效率慢很多。
当时还把上面的代码基于 wxpy 改了一下:

import win32clipboard as win
import win32con
# from requests_html import HTMLSession,sys
# from urllib import parse
from wxpy import *
import time


def gettext():
    win.OpenClipboard()
    text = win.GetClipboardData(win32con.CF_TEXT)
    win.CloseClipboard()
    return text

def initial(bot, chat_obj ,timeout):
    msg_received = False
    msg = ''
    @bot.register(chat_obj)
    def getMsg(in_msg):
        nonlocal msg, msg_received
        msg = in_msg
        msg_received = True

    def main_fun(question):
        nonlocal msg, msg_received
        local_time = time.time()
        msg = ''
        chat_obj.send_msg(question)
        while msg_received != True:
            time.sleep(0.5)
            if time.time() - local_time > timeout:
                return '超时未返回'
        msg_received = False

        return  msg.text
    return main_fun

def getbot():
    bot = Bot(cache_path=True)
    char_obj = bot.mps(update=True)# .search('呆小呆搜题')
    return bot,char_obj

bot, chat_obj = getbot()
getan = initial(bot, chat_obj, 10)
last = ''
while True:
    try:
        temp = str(gettext(), 'gbk')
        pre = temp
        if last != pre:
            print('问题:' + pre)
            # html = HTMLSession().get('http://chati.xuanxiu365.com/index.php?q=' + parse.quote(pre)).html
            # print('\n' + '未查询到您需要的答案...' + '\n' + '\n' if '未查询到' in html.html or '禁止' in html.html else '\n' + html.find('body > form > div.weui-panel > div.weui-panel__bd > div > div', first=True).text + '\n' + '\n')
            print(getan(pre))

            last = pre
    except:
        last = ''
    time.sleep(1)

但是我的微信号不提供 web wx 服务,每次搜题还得找同学,也不方便。

偶然发现了一个可以搜题的网站

进入正题:
有一个同学,他可能网课比我多,就总是抱怨搜题的效率低下,很难受。
今天中午跟他在外边恰饭,期间他在知乎上找到了一个搜题的网站。
当即抓了下包看了看。
【Web】记一次破解 JS 加密_第1张图片
有一个 token字段,感觉有些不妙。

回家之后开始分析:

刚打开就来了个反调试,疯狂断点。
【Web】记一次破解 JS 加密_第2张图片
通过Blackbox script (脚本黑箱化) 解决:
【Web】记一次破解 JS 加密_第3张图片
现在可以调试了,先抓个包:
【Web】记一次破解 JS 加密_第4张图片
除 token 外的两个字段都好说。
type 是前端一个选项决定的。question 就是问题关键字,莫得问题。
现在主要就是这个 token 字段了。

获取 token 字段

这里不要混淆了,此 token 非彼 token,这里的 token 仅仅用于它后端的验证。
首先看一下查询按钮的回调:
【Web】记一次破解 JS 加密_第5张图片
点进去:
发现是直接注册在 Dom 结构上的
在这里插入图片描述
全局搜一下:
发现是请求页面里的函数
【Web】记一次破解 JS 加密_第6张图片
看一下函数体:
【Web】记一次破解 JS 加密_第7张图片
可以看到 token 是通过挂载在 window 下的 jjm 得到的。
接下来就是找这个 jjm。
【Web】记一次破解 JS 加密_第8张图片
全局搜一下,结果没搜到。于是关一下黑箱,下断点单步跟跟一下调用看看:
在这里插入图片描述
跟进去就来到了一个叫 jm.js 的 js 文件:
可以看到,这个文件的代码被混淆过。
框起来的就是被挂载在 window 下的方法,jjm 这个方法名应该是通过 _0x58fb('0x57','rl4#') 返回的。

【Web】记一次破解 JS 加密_第9张图片
通过 chrom 自带的 pretty code 可以格式化代码:
【Web】记一次破解 JS 加密_第10张图片
所有代码复制一下放在 vscode 中,提取这个主加密算法 jjm 在单独文件中,并导入先前的代码。
【Web】记一次破解 JS 加密_第11张图片
观察到仅红框位置涉及外部调用。
绿框处的 md5 加密算法需要额外导入。
接下来我们什么也不调,直接运行一下。
【Web】记一次破解 JS 加密_第12张图片迟迟没有反应,而我本不富裕的内存也雪上加霜,过了一会,堆崩了。
这时猜测 jm.js 中被调用的混淆算法检测了环境,然后通过不断地 new 或者循环的方式把堆给搞崩掉了。
我们下断点单步跟一下看看。
【Web】记一次破解 JS 加密_第13张图片
发现代码永远死在了这个 for 里面。
不过没关系,我发现这个函数没有名字,是自调用的,而且与其他函数完全没有耦合,可以放心删掉。
不过在我删掉后,发现接下来又来了一波看不懂的判断,直接就给我抛了异常。
中间也有着耦合的函数调用,于是我决定放弃这条路,直接渐进式地从主加密算法向外添加函数。
【Web】记一次破解 JS 加密_第14张图片
我们把导入 jm.js 语句删掉,然后抽出唯一的外部调用进来。
这个函数贼长,但是我发现他与外部关联的调用仅此一个,于是我们把它从外边复制进来。
【Web】记一次破解 JS 加密_第15张图片
导入 md5 ,然后直接调这个 jjm 来试一下:
离胜利不远了。
在这里插入图片描述
然后代码又死在了这个 for 里。
在这里插入图片描述
还是太麻烦了,我们回头分析一下主加密算法:
md5 调用的参数使用到了这个高亮的 JSON 对象。可以看到这个对象有两个成员,一个是 iWtcL,另一个是iTvRA,分别对应两个方法。
而这个外部的调用可以看出来是高亮对象的一个字段。那么这个位置不是iWtcL,就是iTvRA。我们直接替换为iTvRA试一下
【Web】记一次破解 JS 加密_第16张图片
直接输出了类似 token 的值,我们用 postman 试一下
在这里插入图片描述
结果人家后端说 token 错误。

在这里插入图片描述
检察一下参数。
刷新一下页面,发现这里的常量变了,看来这个页面是后端渲染的。
【Web】记一次破解 JS 加密_第17张图片
这下我们用这个字符串加密试一下。
在这里插入图片描述
在这里插入图片描述
然后直接拿到了结果:
Unicode 字符,JSON.parse()即可。
【Web】记一次破解 JS 加密_第18张图片

至此就结束了

后面就是,先封装一下加密算法。然后为了解决跨域的问题,自己写个后端包装一下,让别人调。
【Web】记一次破解 JS 加密_第19张图片
我是完成之后来写博客的,所以后端已经完成了。
据说挺 6 --> 带佬的博客
【Web】记一次破解 JS 加密_第20张图片

你可能感兴趣的:(Web)