前段时间已经成功逆向出了学校app的登录接口然后实现了疫情信息自动填报功能并且实现了QQ机器人对填写结果的实时推送
0x01 工具介绍
- Node.js – 可以让JavaScript运行在服务端的运行环境
- puppeteer – Node.js下的前端自动化测试工具
- Vscode – 一款超级好用的编辑器
- mirai – 开源QQ聊天机器人
0x02 基础环境构建
对于Node.js等环境在此不再过多讲述,网上安装教程一大把。直接从安装依赖开始。
首先在项目目录中安装依赖(由于axios设置的请求头信息Content-Type不支持text/plain所以需要安装request)。
npm i axios request puppeteer -S
依赖安装完毕引入根据上次抓到的app登录接口封装的登录方法login()
0x03 编写发送消息机器人
Mirai 是一个在全平台下运行,提供 QQ Android 和 TIM PC 协议支持的高效率机器人库。并且其提供了丰富的插件。本项目使用的是 marai-console和 node-mirai开发插件。
由于本项目只需要将结果发送到指定qq所以通过阅读官方提供的node-mirai的插件源码自行封装一个发送消息的功能。代码如下:
// qq.js
const axios = require('axios')
// 获取版本号
function getVersion(){
return new Promise((resolve,reject)=>{
axios.get('http://域名或ip/about')
.then(res=>{
resolve(res.data);
})
.catch(err=>{
reject('')
})
})
};
// 获取Session
function getAuth(authKey){
return new Promise((resolve,reject)=>{
axios.post('http://域名或ip/auth',{
"authKey": authKey
})
.then(res=>{
resolve(res.data.session)
})
.catch(err=>{
reject('')
})
})
}
// 验证绑定
function getVerify(qq,session){
return new Promise((resolve,reject)=>{
axios.post('http://域名或ip/verify',{
"sessionKey": session,
"qq": qq
})
.then(res=>{
resolve(res.data.msg)
})
.catch(err=>{
reject('')
})
})
}
// 释放绑定
function getRelease(qq,session){
return new Promise((resolve,reject)=>{
axios.post('http://域名或ip/release',{
"sessionKey": session,
"qq": qq
})
.then(res=>{
resolve(res.data.msg)
})
.catch(err=>{
reject('')
})
})
}
// 发送消息
function qqMsg(qq,msg,session){
rPowered by 0x3E5 eturn new Promise((resolve,reject)=>{
axios.post('http://域名或ip/sendFriendMessage',{
"sessionKey": session,
"target": qq,
"messageChain": [{ "type": "Plain", "text":msg }]
})
.then(res=>{
resolve(res.data.msg)
})
.catch(err=>{
reject(1024s.cn '')
1024s.cn })
})
}
async function sendMsg(qq,msg){
const session = await getAuth('设置的Auth');
if(session){
const state = await getVerify('服务端登录的qq',session)
if(state==='success'){
const sendResult = await qqMsg(qq,msg,session);
if(sendResult==='success'){
console.log('[Success] 通知消息发送成功');
}else{
console.log('[Error] 通知消息发送失败');
}
const releaseResult = await getRelease('服务端登录的qq',session);
if(releaseResult==='success'){
console.log('[Success] 机器人释放成功');
}else{
console.log('[Error] 机器人释放失败');
}
}else{
console.log('[Error] 绑定机器人失败');
}
}else{
console.log('[Error] 获取机器人Session失败');
}
}
module.exports=sendMsg
在 mirai-console 的插件目
启动成功后,在命令行中输入 login qq号码 密码
来登录机器人qq,到此qq消息机器人搭建完成
0x04 使用puppeteer实现疫情信息自动填写
puppeteer可以模拟用户操作访问网页填写表单并且支持无头浏览器模式。在通过 npm 安装puppeteer插件时会自动下载一个 chromium 浏览器如果是在linux下运行则会提示缺少依赖可以通过安装相关依赖解决
#依赖库
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y
#字体
yum install ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc -y
// reportSubmit.js
'use strict';
const login = require('./***');
const sendMsg = require('./qq')
const puppeteer = require('puppeteer');
// 自动提交疫情防控表单
async function sendReport({username,password,nativePlace,homeAddr,nowAddr,qq}) {
// 用户登录
let {userI本文来自:1024s.cn d, userName, uniqueCode, encToken} = await login(username,password);
if(userId && userName && uniqueCode && encToken ){
console.log('[Log] 用户登录信息获取成功');
console.log(`[Log] 当前用户学号:${userId}`);
console.log(`[Log] 当前用户姓名:${userName}`);
// 模拟用户操作开启浏览器
// headless:false 即可开启浏览器界面 true则表示无头浏览器
const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox'],headless: true});
// 新建页面
const page = await browser.newPage();
// 设置请求头
await page.setExtraHTTPHeaders({
'access-token':encToken,
'unique-code':uniqueCode
})
1024s.cn // 根据响应头跳转页面
await page.on('response',response=>{
let resHeader = response.headers();
if(resHeader.location){
page.setExtraHTTPHeaders({}) // 清空请求头
1024s.cn }
})
// 跳转链接
await page.goto(`疫情填报接口地址`);
// 等待页面渲染完成
const li = await page.waitForSelector('li').catch(err=>{});
if(!li){
console.log('[Log] 没有获取到疫情防控表');
await browser.close();
return '';
}
// 判断是否已经填写过
const isWrite = await page.$x('//*[@class="no_write"]')
if(isWrite.length){
// 点击跳转
(await page.$x('//*[@class="time"]'))[0].click({delay:200});
await console.log('[Log] 成功进入疫情防控表');
// 等待表单渲染完成
await page.waitForSelector('.form_box');
console.log('[Log] 开始填写疫情防控信息');
// 开始填写表单信息
(await page.$x('//*[@class="form_box"]/div/div[5]/div/input'))[0].type(nativePlace);
console.log(`[Log] 籍贯信息: ${nativePlace}`);
await page.waitFor(1000);
(await page.$x('//*[@class="form_box"]/div/div[6]/div/input'))[0].type(homeAddr);
console.log(`[Log] 家庭地址: ${homeAddr}`);
await page.waitFor(1000);
(await page.$x('//*[@class="form_box"]/div/div[7]/div/input'))[0].type(nowAddr);
console.log(`[Log] 现居地哦: ${nowAddr}`);
await page.waitFor(1000);
for(i = 8; i < 17; i++){
(await page.$x(`//*[@class="form_box"]/div/div[${i}]/div/label/span/input`))[0].click();
await page.waitFor(500);
};
// 提交表单
(await page.$x('//div[@class="WriteTask_Detail"]/div[4]/a'))[0].click({delay:1500});
console.log('[Success] 提交疫情防控信息成功');
await sendMsg(qq,`时间:${new Date()}\n学号:${username}\n姓名:${userName}\n状态:疫情防控报告提交成功`)
本文来自:1024s.cn // 关闭浏览器
await page.waitFor(2000);
await browser.close();
}else{
console.log('[Log] 您已经提交过或撤销报告了');
await sendMsg(qq,`时间:${new Date()}\n学号:${username}\n姓名:${userName}\n状态:您已经提交过或撤销报告了`)
await page.waitFor(500);
await browser.close();
}
}else{
console.log('[Error] 登录失败请检查登录信息');
console.log(`[Error] 当前用户学号: ${username}`);
await sendMsg(qq,`时间:${new Date()}\n学号:${username}\n状态:登录失败请联系管理员检查登录信息`)
}
}
module.exports = sendReport;
0x05 调用疫情自动填写方法设置定时任务
将代码上传到服务器并设置任务定时执行,大功告成!下面附上服务器打印日志以及机器人每天自动推送的