赞
踩
之前刷网课搜题一直用的 “帅搜”,写了个 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 服务,每次搜题还得找同学,也不方便。
进入正题:
有一个同学,他可能网课比我多,就总是抱怨搜题的效率低下,很难受。
今天中午跟他在外边恰饭,期间他在知乎上找到了一个搜题的网站。
当即抓了下包看了看。
有一个 token
字段,感觉有些不妙。
刚打开就来了个反调试,疯狂断点。
通过Blackbox script
(脚本黑箱化) 解决:
现在可以调试了,先抓个包:
除 token 外的两个字段都好说。
type 是前端一个选项决定的。question 就是问题关键字,莫得问题。
现在主要就是这个 token 字段了。
这里不要混淆了,此 token 非彼 token,这里的 token 仅仅用于它后端的验证。
首先看一下查询按钮的回调:
点进去:
发现是直接注册在 Dom 结构上的
全局搜一下:
发现是请求页面里的函数
看一下函数体:
可以看到 token 是通过挂载在 window
下的 jjm
得到的。
接下来就是找这个 jjm。
全局搜一下,结果没搜到。于是关一下黑箱,下断点单步跟跟一下调用看看:
跟进去就来到了一个叫 jm.js 的 js 文件:
可以看到,这个文件的代码被混淆过。
框起来的就是被挂载在 window 下的方法,jjm 这个方法名应该是通过 _0x58fb('0x57','rl4#')
返回的。
通过 chrom 自带的 pretty code 可以格式化代码:
所有代码复制一下放在 vscode 中,提取这个主加密算法 jjm 在单独文件中,并导入先前的代码。
观察到仅红框位置涉及外部调用。
绿框处的 md5 加密算法需要额外导入。
接下来我们什么也不调,直接运行一下。
迟迟没有反应,而我本不富裕的内存也雪上加霜,过了一会,堆崩了。
这时猜测 jm.js 中被调用的混淆算法检测了环境,然后通过不断地 new
或者循环的方式把堆给搞崩掉了。
我们下断点单步跟一下看看。
发现代码永远死在了这个 for 里面。
不过没关系,我发现这个函数没有名字,是自调用的,而且与其他函数完全没有耦合,可以放心删掉。
不过在我删掉后,发现接下来又来了一波看不懂的判断,直接就给我抛了异常。
中间也有着耦合的函数调用,于是我决定放弃这条路,直接渐进式地从主加密算法向外添加函数。
我们把导入 jm.js 语句删掉,然后抽出唯一的外部调用进来。
这个函数贼长,但是我发现他与外部关联的调用仅此一个,于是我们把它从外边复制进来。
导入 md5 ,然后直接调这个 jjm 来试一下:
离胜利不远了。
然后代码又死在了这个 for 里。
还是太麻烦了,我们回头分析一下主加密算法:
md5 调用的参数使用到了这个高亮的 JSON 对象。可以看到这个对象有两个成员,一个是 iWtcL
,另一个是iTvRA
,分别对应两个方法。
而这个外部的调用可以看出来是高亮对象的一个字段。那么这个位置不是iWtcL
,就是iTvRA
。我们直接替换为iTvRA
试一下
直接输出了类似 token 的值,我们用 postman 试一下
结果人家后端说 token 错误。
检察一下参数。
刷新一下页面,发现这里的常量变了,看来这个页面是后端渲染的。
这下我们用这个字符串加密试一下。
然后直接拿到了结果:
Unicode 字符,JSON.parse()
即可。
后面就是,先封装一下加密算法。然后为了解决跨域的问题,自己写个后端包装一下,让别人调。
我是完成之后来写博客的,所以后端已经完成了。
据说挺 6 --> 带佬的博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。