背景
在网上下载了一个Java虚拟机的技术文档,本来是想观摩学习一下,但有个坑,下载下来解压需要密码,资源网站给的压缩密码不对。先试试了一下免费软件,说是免费,等了好久破解了,获取密码竟然收费,一次竟然68块大洋,虽说知识无价,但本着自己能干事自己动手,勤俭持家的态度,还是简单尝试了一下,本次记录一下,以供后续薅羊毛
思路
通过多线程+随机密码 进行逐个尝试,成功后输出密码到文件,中断破解程序,简单粗暴,细节没过多考量,密码复杂度高的时候,性能和效率会是个问题,后续在探讨了
一、项目依赖
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>
二、密码生成
密码需要人工预判一下,根据实际情况,选择对应的规则生成,本次没做过多考虑,有优化
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class PasswordRandom {
//数字
public static final String NUMBER = "0123456789";
//字母
public static final String ALPHABET = "abcdefghijklmnopqrstuvwyxz";
//特殊符号
public static final String SYMBOL = "~!@#$%^&*()_+[]{};,.<>?-=";
/**
* 获取指定的字符
* @param includeNumber 是否包含数字
* @param includeAlphabet 是否包含字母
* @param includeSymbol 是否包含字符
* @param length 字符长度
* @return
*/
public static List<String> getStr(boolean includeNumber, boolean includeAlphabet, boolean includeSymbol, int length) {
List<String> result = new ArrayList<>();
StringBuffer sb = new StringBuffer();
if (includeNumber) {
sb.append(NUMBER);
}
if (includeAlphabet) {
sb.append(ALPHABET);
sb.append(ALPHABET.toUpperCase());
}
if (includeSymbol) {
sb.append(SYMBOL);
}
if (sb.length() <= length) {
result.add(sb.toString());
}
char[] chars = sb.toString().toCharArray();
String[] strings = new String[chars.length];
for (int i = 0; i < chars.length; i++) {
strings[i] = String.valueOf(chars[i]);
}
String[] allLists = getAllLists(strings, length);
return Arrays.asList(allLists);
}
/**
* 获取指定位数的数据集合
* @param elements 基类字符数组
* @param length 指定字符串位数
* @return
*/
public static String[] getAllLists(String[] elements, int length) {
String[] allLists = new String[(int) Math.pow(elements.length, length)];
if (length == 1) {
return elements;
} else {
String[] allSublists = getAllLists(elements, length - 1);
int arrayIndex = 0;
for (int i = 0; i < elements.length; i++) {
for (int j = 0; j < allSublists.length; j++) {
allLists[arrayIndex] = elements[i] + allSublists[j];
arrayIndex++;
}
}
return allLists;
}
}
/**
* 获取全部字符集合,包含数字,字母,特殊字符
* @param length
* @return
*/
public static List<String> getCombinationStr(int length) {
return getStr(true, true, true, length);
}
/**
* 获取数字字符集合
* @param length
* @return
*/
public static List<String> getNumberStr(int length) {
return getStr(true, false, false, length);
}
/**
* 获取字母字符集合
* @param length
* @return
*/
public static List<String> getAlphabetStr(int length) {
return getStr(false, true, false, length);
}
/**
* 获取特殊符号字符集合
*
* @param length
* @return
*/
public static List<String> getSymbolStr(int length) {
return getStr(false, false, true, length);
}
public static void main(String[] args) {
//固定位数的纯数字组合
final List<String> str1 = getNumberStr(4);
System.out.println(str1.toString());
//固定长度的纯字母组合
final List<String> str2 = getAlphabetStr(4);
System.out.println(str2.toString());
//固定长度特殊字符组合
final List<String> str3 = getSymbolStr(4);
System.out.println(str3.toString());
//固定长度随机组合
final List<String> str4 = getCombinationStr(4);
System.out.println(str4.toString());
}
}
三、文件解压
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.FileHeader;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class UnZipUtils {
/**
* @param source 原始文件路径
* @param dest 解压路径
* @param password 解压文件密码
*/
public static boolean unZip(String source, String dest, String password) {
try {
File zipFile = new File(source);
ZipFile zFile = new ZipFile(zipFile);
zFile.setFileNameCharset(StandardCharsets.UTF_8.name());
// 解压目录
File destDir = new File(dest);
// 目标目录不存在时,创建该文件夹
if (!destDir.exists()) {
destDir.mkdirs();
}
if (zFile.isEncrypted()) {
// 设置密码
zFile.setPassword(password.toCharArray());
}
// 将文件抽出到解压目录(解压)
zFile.extractAll(dest);
List<FileHeader> headerList = zFile.getFileHeaders();
File file = null;
for (FileHeader fileHeader : headerList) {
if (!fileHeader.isDirectory()) {
file = new File(destDir, fileHeader.getFileName());
System.out.println(file.getAbsolutePath() + "文件解压成功!");
}
}
} catch (ZipException e) {
return false;
}
return true;
}
}
四、文件处理
import org.apache.commons.lang3.StringUtils;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ZipPwdCracking {
private ThreadPoolExecutor threadPoolExecutor;
//使用线程池进行文件解压尝试处理
public ZipPwdCracking() {
this.threadPoolExecutor = new ThreadPoolExecutor(20, 100, 30,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
//解压处理
public String run(String source, String dest) {
//设置密码使用类型
List<String> numberStr = PasswordRandom.getNumberStr(4);
//创建密码集合队列
LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
numberStr.forEach(num -> queue.offer(num));
//开始时间
long startTime = System.currentTimeMillis();
while (!queue.isEmpty()) {
threadPoolExecutor.execute(() -> {
String name = Thread.currentThread().getName();
String key = queue.poll();
if (StringUtils.isNotBlank(key)) {
//解压
boolean result = UnZipUtils.unZip(source, dest, key);
if (result) {
try (FileWriter fileWriter = new FileWriter(dest + "\\pwd.txt")) {
fileWriter.write(key);
System.out.println("线程:" + name + ",密码是:" + key+",处理结束");
//清除队列
queue.clear();
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("线程:" + name + "," + key + "密码错误");
}
}
});
}
final long endTime = System.currentTimeMillis();
System.out.println("共花费:" + (endTime - startTime) / 1000 + "秒");
return null;
}
//测试主方法
public static void main(String[] args) {
ZipPwdCracking pwdCracking = new ZipPwdCracking();
String source = "D:\\temp\\测试.zip";
String dest = "D:\\temp\\test";
pwdCracking.run(source, dest);
}
}