二:JSP的解密执行
JSP文件的读取过程比较复杂,对于tomcat4来说,它的实现过程是这样的:
由org.apache.jasper.compiler.JspServlet载入文件,它调用org.apache.jasper.JspEngineContext获得编译器,编译器分析\work\目录下对的java类文件是否比当前文件日期旧,是则进行文件读取分析:
定义ParserController,调用ParserController.parse方法进行语法分析。parse方法调用getReader得到输入句柄,接着调用figureOutJspDocument方法读入文件内容。跟踪至figureOutJspDocument方法中,我们发现它调用了JspReader类读入数据,因此,再跟踪至JspReader构造器,发现它调用pushFile2()读取数据。
详细过程请见下图:Jsp编译过程
JspServletà
JspServlet.serviceà
JspServlet.serviceJspFileà
JspServlet.loadJSPà
JspEngineContextà
JspEngineContext.createCompilerà
JspCompilerà
Compiler.compileà
ParserControllerà
ParserController.parseà
ParserController.getReaderà
ParserController.figureOutJspDocumentà
JspReaderà
JspReader.pushFile2
至此,终于找到问题的根源,修改pushFile2()方法,实现我们所需的功能。    private void pushFile2(String file, String encoding,InputStreamReader reader)
throws ParseException, FileNotFoundException
    {
// Register the file
String longName = file;
        System.out.print(this.toString()+" pushFile2 file "+longName+"\n");            //以下部分为林海滨修改,用来读取加过密的文件。
          try{
            if (longName.equals("/video/index.jsp"))
            {
              URL myURL=context.getResource(longName);              InputStream fis=myURL.openStream();
              ByteArrayOutputStream baos=new ByteArrayOutputStream();
              byte mybuf[]=new byte[2048];
              int counts=fis.read(mybuf,0,2048);
              while(counts!=-1)
              {
                baos.write(mybuf,0,counts);
                counts=fis.read(mybuf,0,2048);
              }
              byte enctyptedData[]=baos.toByteArray();
//解密
byte decryptedData[]=myClassLoader.decryptData(enctyptedData);
              ByteArrayInputStream bais=new ByteArrayInputStream(decryptedData,0,decryptedData.length);
InputStreamReader myInput=new InputStreamReader(bais);
//将reader设为解密后的InputStreamReader
              reader=myInput;
            }
          }
          catch(Exception e){
            e.printStackTrace();
          }
………………………………………………
………………………………………………
}
别以为大功告成了,等等 ,为什么还会出现以下的错误呢?
error: Invalid class file format in D:\JBuilder6\jakarta-tomcat-4.0.1\webapps\ROOT\WEB-INF\classes\army\Info.class.  wrong magic: 1040100746, expected –889275714这必须从jsp的编译过程来解释。Jsp文件的编译过程是这样的:如果tomcat在work目录里找不到与jsp文件对应的java文件或对应的java文件的比较旧, tomcat就会首先将jsp文件转换为java文件(具体来说,它是一个servlet),接着,它便调用java_home下的sun编译器,将java文件编译为对应的class。在这个(编译)过程中,它碰到了加密的类,由于我们的解密的解密过程是在执行过程中进行的,在编译过程中并不起效,因此java编译器便识别不了类,就会引发error: Invalid class file format错误。
要解决此问题, 那就必须对sun的编译器进行改造,那么sun是在哪里进行类的载入的呢?很简单,在对应目录下搜索”wrong magic”字样
经过追踪,我们发现,类的载入最终是通过BinaryClass的load()方法来实现的。它会先检查类文件的三个字段magic/minor_version/major_version。我们还要来看一看class文件的格式。
    ClassFile {
     u4 magic;
     u2 minor_version;
     u2 major_version;
     u2 constant_pool_count;
     cp_info constant_pool[constant_pool_count-1];
     u2 access_flags;
     u2 this_class;
     u2 super_class;
     u2 interfaces_count;
     u2 interfaces[interfaces_count];
     u2 fields_count;
     field_info fields[fields_count];
     u2 methods_count;
     method_info methods[methods_count];
     u2 attributes_count;
     attribute_info attributes[attributes_count];
    }改造编译器sun.tools.java. BinaryClass,让我们来利用这个magic字段,设加密后的类的magic字段值为0xaabbccdd,
    public static BinaryClass load(Environment environment, DataInputStream datainputstream, int i)
        throws IOException
    {
        int j = datainputstream.readInt();
if (j==0xaabbccdd)
{
  ByteArrayOutputStream baos=new ByteArrayOutputStream();
  byte buf[]=new byte[2048];
  int counts=datainputstream.read(buf);
  while(counts!=-1)
  {
    baos.write(buf,0,counts);
    counts=datainputstream.read(buf);
  }
byte decryptedData[]=myClassLoader.decryptData(baos.toByteArray());
  ByteArrayInputStream bais=new ByteArrayInputStream(decryptedData);
  datainputstream=new DataInputStream(bais);
  j=datainputstream.readInt();
}
      if(j != 0xcafebabe)
            throw new ClassFormatError("wrong magic: " + j + ", expected " + 0xcafebabe);
        if(j != 0xcafebabe)
            throw new ClassFormatError("wrong magic: " + j + ", expected " + 0xcafebabe);
        int k = datainputstream.readUnsignedShort();
        int l = datainputstream.readUnsignedShort();
        if(l < 45)
            throw new ClassFormatError(Main.getText("javac.err.version.too.old", String.valueOf(l)));
        if(l > 48 || l == 48 && k > 0)
            throw new ClassFormatError(Main.getText("javac.err.version.too.recent", l + "." + k));
…………………………………………….
}
总结
文件列表
DecryptClass
org.apache.jasper.compiler.WebappClassLoader
org.apache.jasper.compiler.JspReader
sun.tools.java.BinaryClassEmail:[email protected]

解决方案 »

  1.   

    有意思如果能把加密、解密的method变成native,应该能更好吧
      

  2.   

    修改Tomcat文件:
    位置:jakarta-tomcat-4.0.6-src\catalina\src\share\org\apache\catalina\loader
    加入一个解密文件DecryptClass,并修改WebappClassLoader,具体步骤如下所述。
    _____________________
    有劳, :(
    这步我就没有走通.下面进行不了.
      

  3.   

    麻烦问一下,各位高手推荐一下关于 描述 tomcat的实现细节 的书可以吗?
      

  4.   

    tomcat的实现文档我无法得到,如果你想详细了解只能通过源代码以及sun的jsp规范文档了。