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

Java 如何在jar文件中动态搜索.class文件?,java,bytecode,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代

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

String classname = "com.mycompany.myproject.Test";
URI uri = Tool.searchClass(jarList, classname);
// decompile .class
...
有这样的示例代码吗

添加: Shell脚本很好,但是java代码中有什么方法可以完成这项工作吗?

Add:我刚刚用
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;
}
/**
*如果找到并指定了文件夹,则按jarFile中指定的类名搜索类
*将.class文件复制到包含整个路径的文件夹中。
*@param classname
*@param-jarFile
*@param dest文件夹
*@返回
*/
公共静态文件搜索压缩文件(字符串类名称、文件jarFile、字符串文件夹){
试一试{
//将类名“.”更改为“/”
if(classname.contains(“.”){
classname=classname.replace(“.”,“/”);
}
JarFile jarF=新的JarFile(JarFile);
枚举jarEntries=jarF.entries();
while(jarEntries.hasMoreElements()){
JarEntry JarEntry=jarEntries.nextElement();
if(jarEntry.getName().indexOf(classname)>=0){
字符串filePath=jarFile.getAbsolutePath();
System.out.println(classname+”位于“+filePath+”--“+jarEntry.getName());
如果(destFolder!=null&&!“.equals(destFolder)){
//如果dest文件夹不存在,则创建文件夹。
文件destF=新文件(destFolder);
如果(!destF.exists()){
destF.mkdirs();
}
File f=新文件(destFolder+File.separator+jarEntry.getName());
如果(!f.getParentFile().exists()){
f、 getParentFile().mkdirs();
}
InputStream is=jarF.getInputStream(jarEntry);
FileOutputStream fos=新java.io.FileOutputStream(f);
while(is.available()>0){
fos.write(is.read());
}
fos.close();
is.close();
返回f;
}
}
}
}捕获(IOE异常){
e、 printStackTrace();
}
System.out.println(“在jar中找不到类”);
返回null;
}

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){            
        }       
    }   
}

我还发现另一个使用类

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){            
        }       
    }   
}

我还找到了另一个使用类的答案

我在~/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文件目录特别大,则可能需要一段时间才能找到。我已经在数百个罐子中使用过它,速度很慢,但很有效。

我在~/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文件目录特别大,则可能需要一段时间才能找到。我已经将它用于数百个jar,速度很慢,但很有效。

我正在使用简单的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

我正在使用简单的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

使用的
loadClass()
方法。

使用的
loadClass()
方法。

EJP的提示是一个参考点。这是一个真实的例子,您有非本地URL,因此创建类加载器、加载类并获取其字节码更为正确。也许是这样的:

    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();
URLClassLoader classLoader=newurlclassloader(list.toArray(newurl[0]);
Class clazz=classLoader.loadClass(路径);
File tmp=new File(System.getProperty(“java.io.tmpdir”)、path.replace(“.”、“”)+“.class”);
FileChannel tmpcchannel=newfileoutputstream(tmp).getChannel();
InputStream是=clazz.getResourceAsStream(“/”+path.replace(“.”,“/”+class”);
tmpcchannel.transferFrom(Channels.newChannel(is),0,Long.MAX_值);
URI=tmp.toURI();

EJP的提示是一个参考点。这是一个真实的例子,您有非本地URL,因此创建类加载器、加载类并获取其字节码更为正确。也许是这样的:

    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();
URLClassLoader classLoader=newurlclassloader(list.toArray(newurl[0]);
Class clazz=classLoader.loadClass(路径);
File tmp=new File(System.getProperty(“java.io.tmpdir”)、path.replace(“.”、“”)+“.class”);
FileChannel tmpcchannel=newfileoutputstream(tmp).getChannel();
InputStream是=clazz.getResourceAsStream(“/”+path.replace(“.”,“/”+class”);
tmpcchannel.transferFrom(Channels.newChannel(is),0,Long.MAX_值);
URI=tmp.toURI();

谢谢你的脚本,但我不擅长bash,如果我想在java代码中使用你的脚本,我该怎么做?输入是jar文件URL和类名,你能把源代码还给我吗?我的脚本名为“decode”,我在命令shell下使用它,如下所示:
$forjarfile in*.jar;解码$jar文件;完成谢谢你的脚本,但我不擅长bash,如果我想在java代码中使用你的脚本,我该怎么做?输入是jar文件URL和类名,你能把源代码还给我吗?我的脚本名为“decode”,我在命令shell下使用它,如下所示:
$forjarfile in*.jar;解码$jar文件;完成谢谢你的回复。我不擅长shell,我必须用java代码调用脚本,因此如果我将第一个参数传递给您:所有jar文件位置拆分为“,”第二个是classname,我如何调用您的脚本并获得java中的.class文件?谢谢您的回复。我不擅长shell,我必须用java代码调用脚本,所以如果我向您传递第一个参数:所有jar文件的位置用“,”分割