声明:本文旨在分享分析问题和解决问题的心路历程,并未对QQ邮箱进行恶意攻击
- 最近想着买个服务器自己搭个邮箱服务,但是当我看到服务器价格还是犹豫了。我放弃这个想法的真正原因是自己搭建的服务不能保证收发率、安全性、可用性、需要一定的财力和人力维护,所以我决定还是用现成的。
- 考虑到域名长度和个人习惯我最终还是决定用QQ邮箱英文邮箱账号,但是我试了好几个都被注册了,但是我又不甘心用太长的,怎么办?
一、分析
当前目标是注册一个尽可能短的QQ英文邮箱账号,人工一个一个试基本不可行,这里很自然想到让计算机帮我批量试。
1、打开注册页面->F12,页面有一个输入框和一个按钮,大概猜测是点击按钮后获取输入框的值然后带上token之类的参数传JSON给后端,后端传回来一个状态码
2、点击Network清空旧请求后,随便输入一个aaa111验证操作一遍、发现已经被注册了,在下面找发现没有Json请求,我猜错了
3、查看这几个成功的请求,发现有一个html的GET请求,请求路径上带有我们输入的参数aaa111!原来是直接从服务端拿到了一个新的页面
4、看他原来还有个验证码,但是不知道为啥给去掉了,而且这边请求时还能收到这个验证码的图片,但是请求参数没有要求带上验证码,那这就更简单了!我们甚至不需要用AI模型对验证码图片进行机器识别。
5、多试几次发现:
- 每次请求只有要查询的参数alias不同,其他的都是固定的
- 每次查询系统都会更新Cookei的qm_verifyimagesession,下一次查询需要带上这次的qm_verifyimagesession
- 账号可用的页面上有恭喜两个字,其他页面均没有这两个字,页面并没有类型的标志
二、解决
经过上面的分析,我们很容易想到一个思路:用程序发送get请求,把所有合法的参数都试验一次,如果收到的html页面里有恭喜两个字就判为没有注册。
1、参数的穷举:查看参数的命名规则如下:
这个问题和全排列很像,而且更简单,没有特别的约束,这里我封装了一个工具类RegisterUtil并实现了一个方法getAllPerm,该方法的方法签名为:getAllPerm(char[] chars, int minLength, int maxLength)
其中char数组包含了我们所有可用的字符,minLength是期望的排列串的最小长度,maxLength是期望的排列串的最大长度,代码如下:
/**
* 获取当前字符数组的所有排列串结果集
* @param chars 排列串可用的字符数组
* @param minLength 排列串最小长度
* @param maxLength 排列串最大长度
* @return 所有符合条件的排列串结果集
*/
public static ArrayList<String> getAllPerm(char[] chars, int minLength, int maxLength) {
//存放所有满足条件的序列
ArrayList<String> result = new ArrayList<>();
for (int i = minLength; i <= maxLength; i++) {
//初始排列串
String temp = "";
//递归获取所有排列
perm(chars, i, temp, result);
}
//返回结果集
return result;
}
getAllPerm方法中的perm(char[] chars, int length, String temp, ArrayList<String> result)
是一个递归方法,第一个参数还是getAllPerm方法中的第一个参数,第二个参数length是(递归深度)排列串的长度,第三个参数temp是当前排列串的中间引用变量,第四个参数result负责存放所有合法排列串的集合。
结合上文列出的命名规则,我在方法体内部加入了所有特殊情况的判断,具体代码如下:
/**
* 递归组合排列串(数字不能作为开头)
* @param chars 排列串可用的字符数组
* @param length 排列串的长度
* @param temp 当前排列串
* @param result 符合条件的排列串结果集合
*/
private static void perm(char[] chars, int length, String temp, ArrayList<String> result) {
//获取当前序列串长度
int len = temp.length();
//递归出口字符串达到规定长度
if(len == length) {
System.out.println(temp);
result.add(temp);
} else {
//从字符数组中依次取出可用字符并充当序列的第temp.length() + 1个字符
for (char c : chars) {
//如果是第一个
if (len == 0) {
//如果不是字母开头
if (!Character.isLowerCase(c)) {
continue;
}
} else {
//如果不是第一个就