Skip to content

Commit 679f85d

Browse files
authored
Add files via upload
1 parent e1a0c7a commit 679f85d

1 file changed

Lines changed: 219 additions & 0 deletions

File tree

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import java.io.*;
2+
import java.nio.file.*;
3+
import java.util.*;
4+
import java.util.concurrent.ExecutorService;
5+
import java.util.concurrent.Executors;
6+
import java.util.concurrent.TimeUnit;
7+
import java.util.regex.*;
8+
import java.util.jar.*;
9+
import java.util.zip.*;
10+
import org.apache.bcel.classfile.ClassParser;
11+
import org.apache.bcel.classfile.JavaClass;
12+
import org.apache.bcel.classfile.Method;
13+
14+
public class JarMethodSearcher {
15+
16+
public static void main(String[] args) {
17+
// if (args.length != 2) {
18+
// System.err.println("Usage: java -jar JarMethodFinder.jar <folderPath> <keyword>");
19+
// return;
20+
// }
21+
//
22+
// String folderPath = args[0];
23+
// String keyword = args[1];
24+
String folderPath = "F:\\yonyou-modules-2\\modules\\aedsm\\lib";
25+
String keyword = "loadAttributes";
26+
27+
try {
28+
List<Path> jarFiles = findJarsRecursively(folderPath);
29+
System.out.printf("找到 %d 个 .jar 文件,正在搜索关键字 \"%s\"...\n", jarFiles.size(), keyword);
30+
31+
// 获取 CPU 核心数作为线程池大小
32+
int threadCount = Runtime.getRuntime().availableProcessors();
33+
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
34+
35+
// 并行处理每个 JAR
36+
for (Path jarFile : jarFiles) {
37+
Path finalJarFile = jarFile;
38+
executor.submit(() -> {
39+
try {
40+
System.out.println("\n🔍 正在处理 JAR:" + finalJarFile.getFileName());
41+
searchInJar(finalJarFile.toString(), keyword);
42+
} catch (Exception e) {
43+
System.err.println("⚠️ 处理失败:" + finalJarFile.getFileName());
44+
}
45+
});
46+
}
47+
48+
executor.shutdown();
49+
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
50+
51+
} catch (Exception e) {
52+
e.printStackTrace();
53+
}
54+
}
55+
56+
// 递归查找所有 .jar 文件
57+
private static List<Path> findJarsRecursively(String rootDir) throws IOException {
58+
List<Path> jars = new ArrayList<>();
59+
Path rootPath = Paths.get(rootDir);
60+
61+
if (!Files.exists(rootPath)) {
62+
System.err.println("路径不存在:" + rootDir);
63+
return jars;
64+
}
65+
66+
Files.walk(rootPath)
67+
.filter(path -> path.toString().toLowerCase().endsWith(".jar"))
68+
.forEach(jars::add);
69+
70+
return jars;
71+
}
72+
73+
// 主流程:字节码扫描 + 反编译搜索双模式
74+
private static void searchInJar(String jarPath, String keyword) throws Exception {
75+
Pattern pattern = Pattern.compile(Pattern.quote(keyword), Pattern.CASE_INSENSITIVE);
76+
77+
Path tempDir = Files.createTempDirectory("decompiled_");
78+
79+
// Step 1: 字节码扫描,获取命中的类名
80+
List<String> matchedClassEntries = scanBytecodeForMethods(jarPath, keyword);
81+
82+
if (matchedClassEntries.isEmpty()) {
83+
System.out.println("⚠️ 未找到匹配项");
84+
deleteDirectory(tempDir);
85+
return;
86+
}
87+
88+
// Step 2: 提取命中的 .class 文件
89+
List<Path> classFiles = extractOnlyMatchedClasses(jarPath, matchedClassEntries, tempDir);
90+
91+
// Step 3: 只反编译命中的类
92+
List<Path> javaFiles = decompileWithCFR(classFiles, tempDir);
93+
94+
// Step 4: 搜索源码上下文
95+
for (Path javaFile : javaFiles) {
96+
searchInFile(javaFile, pattern);
97+
}
98+
99+
deleteDirectory(tempDir);
100+
}
101+
102+
// 新增方法:只提取命中的类文件
103+
private static List<Path> extractOnlyMatchedClasses(String jarPath, List<String> matchedEntries, Path destDir) throws IOException {
104+
List<Path> classFiles = new ArrayList<>();
105+
try (JarFile jarFile = new JarFile(jarPath)) {
106+
for (String entryName : matchedEntries) {
107+
JarEntry entry = jarFile.getJarEntry(entryName);
108+
if (entry != null) {
109+
Path target = destDir.resolve(entryName);
110+
Files.createDirectories(target.getParent());
111+
try (InputStream is = jarFile.getInputStream(entry)) {
112+
Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING);
113+
}
114+
classFiles.add(target);
115+
}
116+
}
117+
}
118+
return classFiles;
119+
}
120+
121+
// 从字节码中提取方法名和签名(支持接口方法)
122+
private static List<String> scanBytecodeForMethods(String jarPath, String keyword) throws Exception {
123+
List<String> matchedClasses = new ArrayList<>();
124+
125+
try (JarFile jar = new JarFile(jarPath)) {
126+
Enumeration<JarEntry> entries = jar.entries();
127+
128+
while (entries.hasMoreElements()) {
129+
JarEntry entry = entries.nextElement();
130+
if (entry.getName().endsWith(".class")) {
131+
try (InputStream is = jar.getInputStream(entry)) {
132+
JavaClass clazz = new ClassParser(is, entry.getName()).parse();
133+
134+
for (Method method : clazz.getMethods()) {
135+
String methodName = method.getName();
136+
String signature = method.getSignature();
137+
if (methodName.toLowerCase().contains(keyword.toLowerCase())) {
138+
System.out.println("【字节码匹配】" + clazz.getClassName() + "." + methodName + signature);
139+
System.out.println(" 来自:" + jarPath);
140+
matchedClasses.add(entry.getName()); // 记录命中的类路径
141+
}
142+
}
143+
}
144+
}
145+
}
146+
}
147+
148+
return matchedClasses;
149+
}
150+
151+
// 使用 CFR 反编译 .class 文件为 .java
152+
private static List<Path> decompileWithCFR(List<Path> classFiles, Path outputDir) throws Exception {
153+
List<Path> javaFiles = new ArrayList<>();
154+
155+
for (Path classFile : classFiles) {
156+
String className = classFile.toString().replace(".class", "");
157+
String cfrPath = "C:\\Users\\13740\\Downloads\\cfr-0.152.jar";
158+
ProcessBuilder pb = new ProcessBuilder("java", "-jar", cfrPath, classFile.toString(), "--outputdir", outputDir.toString());
159+
pb.redirectErrorStream(true);
160+
Process process = pb.start();
161+
162+
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
163+
String line;
164+
while ((line = reader.readLine()) != null) {
165+
// 可选打印日志
166+
}
167+
168+
Path javaFile = outputDir.resolve(className + ".java");
169+
if (Files.exists(javaFile)) {
170+
javaFiles.add(javaFile);
171+
} else {
172+
System.err.println("⚠️ 反编译失败:" + classFile.getFileName());
173+
}
174+
}
175+
176+
return javaFiles;
177+
}
178+
179+
// 在反编译后的 .java 文件中搜索关键字并显示上下文
180+
private static void searchInFile(Path filePath, Pattern pattern) throws IOException {
181+
List<String> lines = Files.readAllLines(filePath);
182+
boolean found = false;
183+
184+
for (int i = 0; i < lines.size(); i++) {
185+
Matcher matcher = pattern.matcher(lines.get(i));
186+
if (matcher.find() && !found) {
187+
found = true;
188+
189+
System.out.println("\n【代码匹配】文件:" + filePath.getFileName());
190+
System.out.println("匹配位置:第 " + (i + 1) + " 行");
191+
System.out.println("上下文(共6行):");
192+
193+
int start = Math.max(0, i - 3);
194+
int end = Math.min(lines.size() - 1, i + 3);
195+
for (int j = start; j <= end; j++) {
196+
String prefix = (j == i) ? ">>> " : " ";
197+
System.out.printf("%4d: %s%s%n", j + 1, prefix, lines.get(j));
198+
}
199+
200+
} else if (matcher.find() && found) {
201+
System.out.println(" ... 其他匹配项已省略");
202+
break;
203+
}
204+
}
205+
}
206+
207+
// 清理临时目录
208+
private static void deleteDirectory(Path path) throws IOException {
209+
if (Files.exists(path)) {
210+
Files.walk(path)
211+
.sorted(Comparator.reverseOrder())
212+
.forEach(p -> {
213+
try {
214+
Files.deleteIfExists(p);
215+
} catch (IOException ignored) {}
216+
});
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)