如何在jar文件中动态搜索.class文件?

时间:2012-07-02 04:11:14

标签: java bytecode

我有一个jar文件的URL列表,我也有整个路径类名,如com.mycompany.myproject.Test,如何在这些jar文件中搜索并获取.class文件?我用它来反编译。

String classname = "com.mycompany.myproject.Test";
URI uri = Tool.searchClass(jarList, classname);
// decompile .class
...

这样的任何示例代码?

添加: Shell脚本很好,但有没有一种方法可以在java代码中完成这项工作?

添加:我刚刚使用java.util.jar.JarFile编写了一个静态方法来处理这个问题,希望这有助于其他人

以下代码经过测试且运行正常:

/**
 * Search the class by classname specified in jarFile, if found and destFolder specified
 * Copy the .class file into destFolder with whole path.
 * @param classname
 * @param jarFile
 * @param destFolder
 * @return
 */
public static File searchCompressedFile(String classname, File jarFile, String destFolder) {
    try {
        // change classname "." to "/"
        if(classname.contains(".")){
            classname = classname.replace(".", "/");
        }

        JarFile jarF = new JarFile(jarFile);
        Enumeration<JarEntry> jarEntries = jarF.entries();

        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            if (jarEntry.getName().indexOf(classname) >= 0) {
                String filePath = jarFile.getAbsolutePath();
                System.out.println(classname + " is in " + filePath + "--" + jarEntry.getName());

                if (destFolder != null && !"".equals(destFolder)) {
                    // Make folder if dest folder not existed.
                    File destF = new File(destFolder);
                    if (!destF.exists()) {
                        destF.mkdirs();
                    }

                    File f = new File(destFolder + File.separator + jarEntry.getName());
                    if(!f.getParentFile().exists()){
                        f.getParentFile().mkdirs();
                    }
                    InputStream is = jarF.getInputStream(jarEntry);
                    FileOutputStream fos = new java.io.FileOutputStream(f);
                    while (is.available() > 0) {
                        fos.write(is.read());
                    }
                    fos.close();
                    is.close();

                    return f;
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    System.out.println("Class not found in jar");
    return null;
}

5 个答案:

答案 0 :(得分:1)

jar文件实际上是.zip。可以使用java.util.zip.ZipInputStream进行搜索,如下所示:

import java.util.zip.ZipInputStream;
import java.util.zip.ZipEntry;

public class Test {

    public static void main(String args[]){ 
        try{
                CodeSource src = Main.class.getProtectionDomain().getCodeSource();
                if( src != null ) {
                  URL jar = src.getLocation();  
                  ZipInputStream zip = new ZipInputStream(jar.openStream());                      
                  ZipEntry zipEntry = null;
                  while((zipEntry = zip.getNextEntry()) != null){
                    String entryName = zipEntry.getName();
                    // do what you want
                  }
                }
        }
        catch (Exception e){            
        }       
    }   
}

我还找到了另一个使用课程JarFile

的答案

答案 1 :(得分:1)

我的〜/ bin文件夹中有一个名为findClass.sh的shell脚本:

echo "Finding $2 in $1."
for jar in `find $1 -name \*.jar`
do
   echo -n "."
   for class in `jar tf $jar`
   do
     if [[ "`echo $class | grep $2`" = ""  ]] 
     then
        grep test /dev/null 
     else 
        echo -e "\n$jar : $class"
     fi
     done

 done

如果您的.jar文件目录特别大,可能需要一段时间才能找到。我已经将它与数百个罐子一起使用了,它很慢但很有效。

答案 2 :(得分:1)

使用java.net.URLClassLoaderloadClass()方法。

答案 3 :(得分:1)

EJP的提示是一个参考点。这是一个真实的案例,你有非本地网址,所以创建一个类加载器,加载一个类,并获得它的字节码是更正确的。也许是这样的:

    URLClassLoader classLoader = new URLClassLoader(list.toArray(new URL[0]));
    Class<?> clazz = classLoader.loadClass(path);
    File tmp = new File(System.getProperty("java.io.tmpdir"), path.replace(".", "_") + ".class");
    FileChannel tmpChannel = new FileOutputStream(tmp).getChannel();
    InputStream is = clazz.getResourceAsStream("/" + path.replace(".", "/") + ".class");
    tmpChannel.transferFrom(Channels.newChannel(is), 0, Long.MAX_VALUE);
    URI uri = tmp.toURI();

答案 4 :(得分:0)

我正在使用简单的bash脚本进行反编译。

#!/bin/bash

if [ -z "$1" ] ; then
    echo "There is no argument please enter jar file name"
    exit -1
fi

FNAME=$1
FNAME_SHORT=`basename $1`
DNAME=${FNAME_SHORT/.*/}_jar

CLASS_DIR=${DNAME}/cls
SRC_DIR=${DNAME}/java

mkdir -p $CLASS_DIR
mkdir -p $SRC_DIR
echo "Unpacking ${FNAME}.."
( cd ${CLASS_DIR} ; unzip -o ../../${FNAME} ) > /dev/null 2>&1 || exit -1
CLASSES="$(cd $DNAME ; find cls -name "*.class" -print)"
if [ ! -z "$CLASSES" ] ; then
    for C in $CLASSES ; do
        CNAME=${C/.*/}
        CNAME=${CNAME#cls/}
        JFILE=$SRC_DIR/${CNAME}.java
        CNAME=${CNAME//\//.}
        if [ -f $JFILE ] ; then
            echo "Skipping $C..."
            continue
        fi
        echo -n "Decompile $C to $DNAME..."
        jad -o -f -d $DNAME/java -lnc -s .java -r $DNAME/$C > /dev/null 2>&1
        if [ $? != 0 ] ; then
            echo "FAILED"
            javap -c -classpath $CLASS_DIR ${CNAME} > $JFILE
        else
            echo "OK"
        fi
    done
fi