精华内容
下载资源
问答
  • Shell脚本实现C语言代码行数统计
    2021-05-15 21:03:57

    写了一个比较粗糙的c语言代码行数统计脚本,目前还有些bug,而且效率也不高。脚本主要就是去除大部分的注释后统计行数,相当于做了一部分预处理的工作。下面是代码:

    #!/bin/bash

    filename=$1

    echo "`whoami`"

    if [ $# -lt 1 ];then

    echo "usage : ./scripts filename"

    exit -1

    fi

    if [ ! -f $filename ];then

    echo "$filename is not a file"

    exit 0;

    fi

    user="`whoami`"

    if [ "$user" != "root" ];then

    echo "use scripts with root"

    exit 0;

    fi

    #删除空行,空行包括:tables + space

    sed '/^[[:blank:]]*$/d' $filename > "${filename}_backup"

    #删除 // 所在的行

    sed -i '/^[[:blank:]]*\/\//d' "${filename}_backup"

    #/*xxxxx*/

    #打印/*所有的开始行

    grep -n '[[:blank:]]*/[*]' "${filename}_backup" | awk 'begin{fs=":"}{print $1}' > "start.txt"

    #打印*/所有的结束行

    grep -n '[*]/[[:blank:]]*' "${filename}_backup" | awk 'begin{fs=":"}{print $1}' > "end.txt"

    #合并两个文件,final.txt里面每行就是/*xx....\n....xx*/这种类型注释的开始行和起始行号

    paste "start.txt" "end.txt" > "final.txt"

    while read line

    do

    #得到起始行

    start=`echo "$line" | awk 'begin{fs="\t"}{print $1}'`

    #得到结束行

    end=`echo "$line" | awk 'begin{fs="\t"}{print $2}'`

    #这里有一个bug,如是/*xxxx*/中的/*和*/在同一行出现,并且有代码也在同一行

    # printf("hello world\n"); /*打印字符串*/

    # /*打印字符串*/

    #上面两种情况的就没法判断,希望大家优化

    if [ $start -eq $end ];then

    continue

    fi

    #删除/*到*/中间所有行

    sed -i "${start},${end}d" "${filename}_backup"

    done < "final.txt"

    wc -l "${filename}_backup"

    rm -f "final.txt" "start.txt" "end.txt"

    脚本只能实现大概的代码行数的统计,不能做到精确统计!

    因为没有做很多的测试,而且也不排除有些比较少见的注释我没考虑到,另外脚本的效率也比较差,用了很多tmp文件。

    所以希望大家多提提意见!

    更多相关内容
  • 使用NotePad++可以快速去除注释和空行,方法如下: 去除// 去除多行注释 正则表达式 /\*{1,2}[\s\S]*?\*/ 去除空行

    使用NotePad++可以快速去除注释和空行,方法如下:

    去除//

    去除多行注释

    正则表达式    /\*{1,2}[\s\S]*?\*/

    去除空行

     

    展开全文
  • CalcRowCnt程序源码,统计VC++工程的源代码行数,可选择是否统计空行,是否统计注释行。 运行环境:Windows/Visual C/C++
  • 计算指定目录下源代码的有效行数,也显示出空行的行数,注释的行数。是vc2005建立的工程,下载后可直接调试及运行。
  • 代码行统计工具用Java语言实现,可以对C\C++、Java的源代码进行统计,统计结果包括:总行数、空行数、注释行数代码行数,统计结果可以以表格的形式显示给用户或另存为文件。 一 执行环境: 操作系统:WindowsNT/...
  • 统计代码行数

    2013-06-21 14:53:44
    附带源码的程序,主要功能是统计VC工程的源代码行数,可选择是否统计空行,是否统计注释行.
  • [笔记]如何在VC中调试SQLite源代码 心血来潮下载了SQLite源代码(版本 3.7.12.1),解压后只有4个文件:sqlite3.h,sqlite3ext.h,shell.c,sqlite3.c,简单粗暴我喜欢。 按照文档介绍,写了个最简单的demo程序如下...

    [笔记]如何在VC中调试SQLite源代码

    心血来潮下载了SQLite源代码(版本 3.7.12.1),解压后只有4个文件:sqlite3.h,sqlite3ext.h,shell.c,sqlite3.c,简单粗暴我喜欢。

    按照文档介绍,写了个最简单的demo程序如下:

    复制代码

    // The name of a database is given by the first argument and 
    // the second argument is one or more SQL statements to execute against the database.
    
    #include <stdio.h>
    #include "sqlite3.h"
    
    static int callback(void *NotUsed, int argc, char **argv, char **azColName){
        int i;
        for(i=0; i<argc; i++){
            printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
        }
        printf("\n");
        return 0;
    }
    
    int main(int argc, char **argv){
        sqlite3 *db;
        char *zErrMsg = 0;
        int rc;
    
        if( argc!=3 ){
            fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
            return(1);
        }
    
        rc = sqlite3_open(argv[1], &db);
        if( rc ){
            fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
            sqlite3_close(db);
            return(1);
        }
        rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
        if( rc!=SQLITE_OK ){
            fprintf(stderr, "SQL error: %s\n", zErrMsg);
            sqlite3_free(zErrMsg);
        }
        sqlite3_close(db);
    
        return 0;
    }

    复制代码

    想在VC2010中调试下sqlite3.c,就在调用sqlite3_open()处打了个断点,结果发现跟进去的位置好古怪,完全不是正确的地方。

    搜到liigo的一篇文章“VC6/VC2005均不支持行数超过65536的C/C++源代码文件”,而当前sqlite3.c文件共136692行,看来VC2010仍然不支持过长的文件。

    为了将sqlite3.c的行数压缩到65535行以内,有如下2个方案:

     

    方案1:删除注释行、空行、#if 0 ... #endif

    仅将注释行删除是不够的,即使将空行都删除也不够,再加上删除#if 0 ... #endif,勉强挤到了66675行。

    懂正则表达式的话,上述操作其实不难。

    虽然仍然超出65535一些,但是只要调试的代码行不要太靠后,大体上还是满足调试需求的。

     

    方案2:拆分源文件为几部分,再通过#include的方式将其嵌入

    源文件sqlite3.c文件共136692行,因为136692/65535=2.086,所以可以将此文件拆为3部分,主文件sqlite3.c,及两个子文件 sqlite3_1.h 和 sqlite3_1.h,调用方法为主文件sqlite3.c在最后#include另外两个子文件,如下:

    ...
    #include "sqlite3_1.h"
    #include "sqlite3_2.h"

     

    为什么sqlite3.c文件这么大?

    根据官网的说法,该文件是将多个文件(Sqlite核心文件及FTS3 和 RTREE 扩展)整合(amalgamation)为一个单独的文件的,据说这样可以让编译器达成更充分的优化,从而提高5%-10%的效率。

     

    代码下载

    为方便下载,我已经将Sqlite源代码(sqlite-amalgamation-3071201) + 方案1(sqlite3_no_comment) + 方案2(sqlite3_split)的代码打包上传在这里了。

     

    展开全文
  • VC6上经典的插件

    2013-08-27 17:35:10
    WndTabs主要是在编辑窗口中显示了所有已经打开的文件,在VC中能够更方便的操作这些文件,比如修改文件属性,copy文件路径、文件名等,并且还开放源代码,你要是愿意的话,可以添加自己很兴趣的功能。 3. Line...
  • VC6插件系列

    2014-07-01 15:13:55
    功能:WndTabs主要是在编辑窗口中显示了所有已经打开的文件,在VC中能够更方便的操作这些文件,比如修改文件属性,copy文件路径、文件名等,并且还开放源代码,你要是愿意的话,可以添加自己很兴趣的功能。...
  • Spring项目源代码加密

    千次阅读 2019-03-13 23:41:58
    公司项目要求进行源代码加密,防止他人进行反编译(毕竟项目要运行在客户的机器上)。项目框架采用的是:Spring + Spring MVC + Spring Data JPA。可在网上查阅资料,关于Spring项目源代码加密的内容不多,也没找到...

    前言

    公司项目要求进行源代码加密,防止他人进行反编译(毕竟项目要运行在客户的机器上)。项目框架采用的是:Spring + Spring MVC + Spring Data JPA。可在网上查阅资料,关于Spring项目源代码加密的内容不多,也没找到什么现成的工具。所以,只能自己动手写加密代码了。过程几经坎坷,在此进行记录一下,也希望能帮到有相同需求的朋友。

    思路

    写工具类手动对项目指定包下生成的class文件内容进行加密,在容器框架加载class文件时解密获取正确的字节码。头疼的来了,不少地方都对字节码文件进行了读取解析,要改不少源代码。

    准备工作

    1、准备JDK1.8源码(我电脑配置的JDK为1.8.0_131)。在JDK安装目录下有个src.zip,那个就是当前版本JDK的源码

    2、准备Tomcat7源码。(需要安装ant进行项目构建)

    3、准备Spring源码。这个下载的框架里面自带的

    4、准备hibernate-entitymanager源码

    5、准备aspectjweaver源码(项目中切面编程做日志) 

    5、一个加密解密的工具类jar包。用于对生成的class文件进行加密,在我们修改后的tomcat、spring中需要调用这个类对已加密的class进行解密。另外这个jar包最后需要使用加密锁进行壳加密以保证加解密代码的安全

    6、写一个读取配置文件的工具类。配置文件中记录需要解密的包名、路径地址、是否执行解密操作(便于开发时调试)等信息
     

    二、需要修改的类 

     

    1、JDK中需要修改的类

    a)    java.io.FileInputStream:修改后覆盖到rt.jar中对应包里的class文件

    2、Tomcat中需要修改的类

    a)    org.apache.tomcat.util.bcel.classfile.ClassParser:修改后覆盖到tomcat-coyote.jar中对应包里的class文件

    b)    org.apache.catalina.loader.WebappClassLoader:修改后覆盖到catalina.jar中对应包里的class文件

    3、Spring中需要修改的类

    a)    org.springframework.core.type.classreading.SimpleMetadataReader:修改后覆盖到spring-core-4.3.7.RELEASE.jar中对应包里的class文件

    4、hibernate-entitymanager中需要修改的类

    a)   org.hibernate.ejb.packaging.AbstractJarVisitor:修改后覆盖hibernate-entitymanager-4.1.8.Final.jar中对应包里的class文件

    b)   org.hibernate.ejb.packaging.ExplodedJarVisitor:修改后覆盖hibernate-entitymanager-4.1.8.Final.jar中对应包里的class文件

    5、aspectjweaver中需要修改的类

    a)   org.aspectj.apache.bcel.classfile.ClassParser:修改后覆盖aspectjweaver.jar中对应包里的class文件

    b)   org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository:修改后覆盖aspectjweaver.jar中对应包里的class文件

    三、进行修改 

     1、修改JDK提供的java.io.FileInputStream类

    目的:提供方法获取class文件的路径,这样才能做后面的解码。

        /**
         * The path of the referenced file
         * (null if the stream is created with a file descriptor)
         */
        private final String path;
    
        /**
         * 
         * @title: getPath
         * @description: 提供方法让外部能够获取到当前文件路径
         * @author: 陈家宝
         * @version: V1.00
         * @date: 2019年3月11日 下午1:07:44
         * @return
         */
        public String getPath() {
    		return path;
    	}

     

     2、修改Tomcat的org.apache.tomcat.util.bcel.classfile.ClassParser类

    对ClassParser方法进行重写。目的:对配置文件中要求进行解密的class文件经过解密后,再将流给Tomcat。

    原方法

    public ClassParser(final InputStream inputStream) {
        this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE));
    }
    

    修改后

        public ClassParser(final InputStream inputStream) {
        	// TODO 判断该类是否需要解密
        	InputStream newInputStream = inputStream;
        	
        	try {
    			//DecodeConf是记录配置信息的类
    			//DecodeConf.isRunDecode:记录是否需要执行解密操作
    			if (DecodeConf.getConf().isRunDecode() && inputStream instanceof FileInputStream) {
    				// 配置中设置为需要解密,从文件流获取文件路径,用于判断是否为需要解密的类
    				// FileInputStream.getPath()为修改JDK源码而来,所有要用我的JDK
    				String path = ((FileInputStream)inputStream).getPath();
    				FileOperateHelper.log("ClassParser path=" + path, true);
    				/*
    	               *DecodeConf.dirs:所有需要解密的文件路径(配置文件中记录到目录这层,根据需要可以明确到文件)集合
    	               *dirs是一个集合对象,记录了所有需要解密的目录
    	               *我配置文件中记录的是相对路径,只到包名这层
    	               *如包名为com.abc.service则记录的目录路径为/com/abc/service/
    	               *判断当前目录是否需要解密
    	              	*/
    				for(String dir : DecodeConf.getConf().getDirs()) {
    					// 统一文件路径分隔符
    					dir = dir.replace("/", "\\");
    					FileOperateHelper.log("ClassParser dir=" + dir, true);
    					if (path.indexOf(dir) != -1) {
    						// 该类在需要解密的目录下,进行解密
    						// 读取文件内容
    						byte[] oldcontent = FileOperateHelper.read(new File(path));
    						// 进行解密
    						byte[] decodeByte = EncodeUtil.simpleDecrypt(oldcontent);
    						
    						newInputStream = new ByteArrayInputStream(decodeByte);
    						break;
    					}
    				}
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
        	
        	// 不管上面有没有解密,用newInputStream生成DataInputStream
            this.dataInputStream = new DataInputStream(new BufferedInputStream(newInputStream, BUFSIZE));
        }

    3、修改Tomcat的org.apache.catalina.loader.WebappClassLoader类

    在类中重写下findClass方法

        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException {
    
        	/**
        	 * 类加载器的三个机制:委托、单一性、可见性
        	 * 委托:指加载一个类的请求交给父类加载器,若父类加载器不可以找到或者加载到,再加载这个类
        	 * 单一性:指子类加载器不会再次加载父类加载器已经加载过的类
        	 * 可见性:子类加载器可以看见父类加载器加载的所有类,而父类加载器不可以看见子类加载器所加载的类
        	 * 
        	 * 所以,return super.findClass(name);是可能加载不到类的(可能有些类需要子类加载器才加载到),
        	 * 即意味着可能会产生ClassNotfoundException异常,所以不能放在try catch代码块里边,因为调用者需要知道是否成功加载。
        	 */
        	// 如果配置文件中配置了不执行解密,直接调用父类的findClass
        	if (!DecodeConf.getConf().isRunDecode()) {
        		return super.findClass(name);
        	}
        	
        	try {
    			// TODO 判断当前类是否需要解密
    			// 判断当前类是否在需要解密的包路径之下
    			for(String pkg : DecodeConf.getConf().getPackages()) {
    				if (name.indexOf(pkg) != -1) {
    					// 将类名称转换为文件路径及文件名
    					String fileName = name.replace(".", "/") + ".class";
    					// 根据文件路径,从已加载的类的ResourceEntry集合中获取指定的ResourceEntry
    					ResourceEntry resourceEntry = resourceEntries.get("/" + fileName);
    					// 拼接class文件绝对路径,当然,ResourceEntry中也有文件的绝对路径
    					String basePath = DecodeConf.getConf().getClassPath();
    					String classPath = basePath + fileName;
    					// 但是,内部类获取不到ResourceEntry(不确定是不是全部的内部类都获取不到),如果获取得到就用ResourceEntry中的路径
    					if (resourceEntry != null) {
    						// 如果路径中带空格会变成"%20"导致无法成功获取文件,进行处理
    						classPath = URLDecoder.decode(resourceEntry.source.getPath(), "UTF-8");
    						if (classPath.startsWith("/")) {
    							classPath = classPath.substring(1);
    						}
    					}
    					
    					File classFile = new File(classPath);
    					// 如果文件存在,则进行解密
    					if (classFile.exists() && classFile.isFile()) {
    						InputStream is = StreamDecode.decode(new FileInputStream(classFile));
    						// 将解密后的流包含的内容,读取到字节数组
    						byte[] b = new byte[is.available()];
    						is.read(b);
    						// 将字节数组内容转换成Class对象
    						return defineClass(b, 0, b.length);
    					}
    				}
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
        	
        	return super.findClass(name);
        }

    4、修改Spring的org.springframework.core.type.classreading.SimpleMetadataReader类

    原方法

    SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
        InputStream is = new BufferedInputStream(resource.getInputStream());
        ClassReader classReader;
        try {
            classReader = new ClassReader(is);
        }
        catch (IllegalArgumentException ex) {
            throw new NestedIOException("ASM ClassReader failed to parse class file - " +
            "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
        }
        finally {
            is.close();
        }
        AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
        classReader.accept(visitor, ClassReader.SKIP_DEBUG);
        this.annotationMetadata = visitor;
        // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
        this.classMetadata = visitor;
        this.resource = resource;
    }
    

    修改后

    SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
    		InputStream is = new BufferedInputStream(resource.getInputStream());
    		InputStream newInputStream = is;
    		ClassReader classReader;
    		try {
    			try {
    				//TODO 判断是否需要解密
    				FileOperateHelper.log("isRunDecode() = " + String.valueOf(DecodeConf.getConf().isRunDecode()), true);
    				if(DecodeConf.getConf().isRunDecode()) {
    					for(int i=0; i<DecodeConf.getConf().getDirs().length; i++) {
    						String dir = DecodeConf.getConf().getDirs()[i];
    						 String filepath = "";
    				        // 获取文件路径,判断是否为需要解密的class(即该class在需要解密的路径下)
    				        if (resource instanceof FileSystemResource) {
    							filepath = ((FileSystemResource)resource).getPath();
    						}else if (resource instanceof ClassPathResource) {
    							filepath = ((ClassPathResource)resource).getPath();
    						}
    				        
    				        FileOperateHelper.log("filepath = " + filepath, true);
    				        // 统一文件路径分隔符
    						// dir = dir.replace("\\", "/");
    						FileOperateHelper.log("dir = " + dir, true);
    				        if (filepath.indexOf(dir) != -1) {
    				        	FileOperateHelper.log("try to decrypt: " + filepath, true);
    							is = StreamDecode.decode(is);
    							break;
    						}
    					}
    				}
    			} catch (Exception e) {
    				throw new RuntimeException("解密出错!");
    			}
    
    			classReader = new ClassReader(is);
    		}
    		catch (IllegalArgumentException ex) {
    			throw new NestedIOException("ASM ClassReader failed to parse class file - " +
    					"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
    		}
    		finally {
    			is.close();
    		}
    
    		AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
    		classReader.accept(visitor, ClassReader.SKIP_DEBUG);
    
    		this.annotationMetadata = visitor;
    		// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
    		this.classMetadata = visitor;
    		this.resource = resource;
    	}

    5、修改hibernate-entitymanager中的org.hibernate.ejb.packaging.AbstractJarVisitor类

    原方法

    private boolean checkAnnotationMatching(InputStream is, JavaElementFilter filter) throws IOException {
    		if ( filter.getAnnotations().length == 0 ) {
    			is.close();
    			return true;
    		}
    
    		DataInputStream dstream = new DataInputStream( is);
    		ClassFile cf = null;
    
    		try {
    			cf = new ClassFile( dstream );
    		}
    		finally {
    			dstream.close();
    			is.close();
    		}
    		boolean match = false;
    		AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag );
    		if ( visible != null ) {
    			for ( Class annotation : filter.getAnnotations() ) {
    				match = visible.getAnnotation( annotation.getName() ) != null;
    				if ( match ) break;
    			}
    		}
    		return match;
    	}

    修改后

    private boolean checkAnnotationMatching(InputStream is, JavaElementFilter filter) throws IOException {
    		if ( filter.getAnnotations().length == 0 ) {
    			is.close();
    			return true;
    		}
    
    		InputStream newInputStream = is;
    		// TODO 检查该类是否需要进行解密
    		try {
    			//DecodeConf是记录配置信息的类
    			//DecodeConf.isRunDecode:记录是否需要执行解密操作
    			FileOperateHelper.log("AbstractJarVisitor isRunDecode()=" + String.valueOf(DecodeConf.getConf().isRunDecode()), true);
    			FileOperateHelper.log("AbstractJarVisitor inputStream tpye: " + is.getClass().getSimpleName(), true);
    			if (DecodeConf.getConf().isRunDecode() && is instanceof MyFileInputStream) {
    				// 配置中设置为需要解密,从文件流获取文件路径,用于判断是否为需要解密的类
    				// FileInputStream.getPath()为修改JDK源码而来,所有要用我的JDK
    				String path = "";
    				MyFileInputStream fis = (MyFileInputStream)is;
    				path = fis.getPath();
    				FileOperateHelper.log("AbstractJarVisitor path=" + path, true);
    				/*
    			       *DecodeConf.dirs:所有需要解密的文件路径(配置文件中记录到目录这层,根据需要可以明确到文件)集合
    			       *dirs是一个集合对象,记录了所有需要解密的目录
    			       *我配置文件中记录的是相对路径,只到包名这层
    			       *如包名为com.abc.service则记录的目录路径为\\com\\abc\\service\\
    			       *判断当前目录是否需要解密
    		      	 */
    				for(String dir : DecodeConf.getConf().getDirs()) {
    					// 统一文件路径分隔符
    					dir = dir.replace("/", "\\");
    					FileOperateHelper.log("AbstractJarVisitor dir=" + dir, true);
    					if (path.indexOf(dir) != -1) {
    						// 该类在需要解密的目录下,进行解密
    						// 读取文件内容
    						byte[] oldcontent = FileOperateHelper.read(new File(path));
    						// 进行解密
    						byte[] decodeByte = EncodeUtil.simpleDecrypt(oldcontent);
    						
    						newInputStream = new ByteArrayInputStream(decodeByte);
    						break;
    					}
    				}
    			}
    		} catch (Exception e) {
    			FileOperateHelper.log("AbstractJarVisitor transform inputStream error: " + e.getMessage(), true);
    			e.printStackTrace();
    		}
    
    
    		DataInputStream dstream = new DataInputStream( newInputStream );
    		ClassFile cf = null;
    
    		try {
    			cf = new ClassFile( dstream );
    		}
    		finally {
    			dstream.close();
    			is.close();
    		}
    		boolean match = false;
    		AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag );
    		if ( visible != null ) {
    			for ( Class annotation : filter.getAnnotations() ) {
    				match = visible.getAnnotation( annotation.getName() ) != null;
    				if ( match ) break;
    			}
    		}
    		return match;
    	}

    6、修改hibernate-entitymanager中的org.hibernate.ejb.packaging.ExplodedJarVisitor类

    原方法

    private void getClassNamesInTree(File jarFile, String header)
        throws IOException
      {
        File[] files = jarFile.listFiles();
        header = header + "/";
        File[] arr$ = files; int len$ = arr$.length; for (int i$ = 0; i$ < len$; ++i$) { File localFile = arr$[i$];
          if (!(localFile.isDirectory())) {
            String entryName = localFile.getName();
            addElement(header + entryName, new BufferedInputStream(new FileInputStream(localFile)), new BufferedInputStream(new FileInputStream(localFile)));
          }
          else
          {
            getClassNamesInTree(localFile, header + localFile.getName());
          }
        }
      }

    修改后

    private void getClassNamesInTree(File jarFile, String header) throws IOException {
    		File[] files = jarFile.listFiles();
    		header = header == null ? "" : header + "/";
    		for ( File localFile : files ) {
    			if ( !localFile.isDirectory() ) {
    				String entryName = localFile.getName();
                    // MyFileInputStream继承自java.io.FileInputStream,这样才能通过getPath()拿到文件路径
    				addElement(
    						header + entryName,
    						new MyFileInputStream( localFile ),
    						new MyFileInputStream( localFile )
    				);
    
    			}
    			else {
    				getClassNamesInTree( localFile, header + localFile.getName() );
    			}
    		}
    	}

    7、修改aspectjweaver中的org.aspectj.apache.bcel.classfile.ClassParser类

    原方法

      /** Parse class from the given stream */
      public ClassParser(InputStream file, String filename) {
        this.filename = filename;
    
        if (file instanceof DataInputStream) this.file = (DataInputStream)file;
        else                                 this.file = new DataInputStream(new BufferedInputStream(file,BUFSIZE));
      }
    
      public ClassParser(ByteArrayInputStream baos, String filename) {
    	    this.filename = filename;
    	    this.file = new DataInputStream(baos);
      }
    
      /** Parse class from given .class file */
      public ClassParser(String file_name) throws IOException {    
        this.filename = file_name;
        file = new DataInputStream(new BufferedInputStream(new FileInputStream(file_name),BUFSIZE));
      }

    修改后

      /** Parse class from the given stream */
      public ClassParser(InputStream file, String filename) {
        this.filename = filename;
    
        // 判断是否需要对流进行解密
        file = decryptStream(file);
    
        if (file instanceof DataInputStream) this.file = (DataInputStream)file;
        else                                 this.file = new DataInputStream(new BufferedInputStream(file,BUFSIZE));
      }
    
      public ClassParser(ByteArrayInputStream baos, String filename) {
    	    this.filename = filename;
    	    this.file = new DataInputStream(baos);
      }
    
      /** Parse class from given .class file */
      public ClassParser(String file_name) throws IOException {    
        this.filename = file_name;
        MyFileInputStream mis = new MyFileInputStream(new File(file_name));
        // 判断是否需要对流进行解密
        InputStream is = decryptStream(mis);
        file = new DataInputStream(new BufferedInputStream(is,BUFSIZE));
      }
    
      // 判断是否需要解密,对要解密的流进行解密操作
      public InputStream decryptStream(InputStream inputStream) {
        InputStream newInputStream = inputStream;
          
          try {
          //DecodeConf是记录配置信息的类
          //DecodeConf.isRunDecode:记录是否需要执行解密操作
          if (DecodeConf.getConf().isRunDecode() && inputStream instanceof MyFileInputStream) {
            // 配置中设置为需要解密,从文件流获取文件路径,用于判断是否为需要解密的类
            // FileInputStream.getPath()为修改JDK源码而来,所有要用我的JDK
            String path = ((MyFileInputStream)inputStream).getPath();
            FileOperateHelper.log("aspectjweaver  ClassParser path=" + path, true);
            /*
             *DecodeConf.dirs:所有需要解密的文件路径(配置文件中记录到目录这层,根据需要可以明确到文件)集合
             *dirs是一个集合对象,记录了所有需要解密的目录
             *我配置文件中记录的是相对路径,只到包名这层
             *如包名为com.abc.service则记录的目录路径为\\com\\abc\\service\\
             *判断当前目录是否需要解密
               */
            for(String dir : DecodeConf.getConf().getDirs()) {
              // 统一文件路径分隔符
              dir = dir.replace("/", "\\");
              FileOperateHelper.log("aspectjweaver   ClassParser dir=" + dir, true);
              if (path.indexOf(dir) != -1) {
                // 该类在需要解密的目录下,进行解密
                // 读取文件内容
                byte[] oldcontent = FileOperateHelper.read(new File(path));
                // 进行解密
                byte[] decodeByte = EncodeUtil.simpleDecrypt(oldcontent);
                
                newInputStream = new ByteArrayInputStream(decodeByte);
                return newInputStream;
              }
            }
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
    
        return null;
      }

    8、修改aspectjweaver中的org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository类

    原方法

    	private JavaClass loadJavaClass(String className) throws ClassNotFoundException {
    		String classFile = className.replace('.', '/');
    		try {
    			 InputStream is = loaderRef.getClassLoader().getResourceAsStream(classFile + ".class");
    			
    			if (is == null) {
    				throw new ClassNotFoundException(className + " not found.");
    			}
    
    			ClassParser parser = new ClassParser(is, className);
    			return parser.parse();
    		} catch (IOException e) {
    			throw new ClassNotFoundException(e.toString());
    		}
    	}

    修改后

    	private JavaClass loadJavaClass(String className) throws ClassNotFoundException {
    		String classFile = className.replace('.', '/');
    		try {
    			// InputStream is = loaderRef.getClassLoader().getResourceAsStream(classFile + ".class");
    			//TODO 命令行编译时,FileInputStream.getPath()方法获取不到,所以这里替换成我的MyFileInputStream
    			URL url = loaderRef.getClassLoader().getResource(classFile + ".class");
    			// 空格会显示为"%20",导致获取不到文件
    			String path = url.getPath();
    			path = URLDecoder.decode(path, "UTF-8");
    			MyFileInputStream is = new MyFileInputStream(new File(path));
    
    			if (is == null) {
    				throw new ClassNotFoundException(className + " not found.");
    			}
    
    			ClassParser parser = new ClassParser(is, className);
    			return parser.parse();
    		} catch (IOException e) {
    			throw new ClassNotFoundException(e.toString());
    		}
    	}

     

    四、关于修改的源码编译问题

    我的话,Tomcat7用myeclipse搭建了源码项目,所以编译不成问题。其他的源码,修改过后用命令行对修改的java文件进行编译。示例如下:

    C:\Users\Administrator\Desktop\codeEncrypt\aspectjweaver\aspectjweaver-1.7.2-sou
    rces\org\aspectj\apache\bcel\util>javac -encoding utf-8 -Djava.ext.dirs=C:\Users
    \Administrator\Desktop\codeEncrypt\aspectjweaver\needJar NonCachingClassLoaderRe
    pository.java

     解释:-encoding   指定源文件使用的字符编码。因为文件中使用到中文注释,这里指定utf-8;

               -Djava.ext.dirs   指定编译时要引用到的jar包路径,建议把所有引用到的jar包放在同一个文件夹

    另外,命令行编译java文件时,不知道为什么,获取不到JDK中为java.io.FileInputStream添加的getPath()方法,编译无法通过。我确定已经替换了当前配置的JDK的FileInputStream.class文件。而且,Tomcat7的源码修改中,也用到了该方法。但是用myeclipse编译,并没有问题。最后,采取的解决方案是:新建一个类MyFileInputStream,继承自java.io.FileInputSteam。这样,在编译时就没有问题了。无法理解,希望知道缘由的朋友给予指点。

     

    五、配置文件

    通过配置文件记录加/解密的信息,放置在tomcat的lib目录下。例如:

    文件内容:

    #是否运行解密
    isRunDecode=true
    #加密文件相对目录(就是包名转成的目录),多个用“,”分割
    #dirs=\\com\\smt\\iptv\\portal\\web\\json
    dirs=/com/smt/iptv/portal/web/json
    #加密文件的包名,多个用“,”分割
    packages=com.smt.iptv.portal.web.json
    #加密文件的绝对路径
    classPath=C:\\Develop\\apache-tomcat-7.0.93\\webapps\\iptvmanager_maven_test\\WEB-INF\\classes

     

    下面是的工具类、加/解密类等,建议新建java项目,放在一起,打成jar包,编译修改的源码时要用到。另外,也要放一份在Tomcat的lib目录,项目运行需要。

     读取配置文件的工具类:

    package com.jack.vesoft.classencrypt.classreading;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Properties;
    
    /**
     * 
     * Title: DecodeConf
     * Description: 读取配置文件
     * @author 陈家宝
     * @date   2019年3月11日 下午1:50:31
     *
     */
    public class DecodeConf {
    	
    	private Properties props = new Properties();
    	
    	private boolean isRunDecode = false;
    	
    	private String[] dirs;		// 需要解密的路径(包的文件夹层级)
    	
    	private String[] packages;	// 需要解密的包
    	
    	private String classPath;	// 加密文件的绝对路径
    	
    	private static DecodeConf decodeConf;
    	
    	public Properties getProps(){
    		return props;
    	}
    	
    	public boolean isRunDecode() {
    		return isRunDecode;
    	}
     
    	public void setRunDecode(boolean isRunDecode) {
    		this.isRunDecode = isRunDecode;
    	}
     
    	public String[] getDirs() {
    		return dirs;
    	}
     
    	public void setDirs(String[] dirs) {
    		this.dirs = dirs;
    	}
     
    	public String[] getPackages() {
    		return packages;
    	}
     
    	public void setPackages(String[] packages) {
    		this.packages = packages;
    	}
     
    	public String getClassPath() {
    		return classPath;
    	}
     
    	public void setClassPath(String classPath) {
    		this.classPath = classPath;
    	}
    	
    	public static DecodeConf getConf(){
    		return decodeConf;
    	}
     
        // 默认读取类路径下的decode.properties文件,所以此工具类所在的jar包,运行时要放在Tomcat的lib目录,decode.properties文件也要放在这里
    	static{
    		decodeConf = new DecodeConf();
    		InputStream is = DecodeConf.class.getClassLoader().getResourceAsStream("decode.properties");
    		if (is != null) {
    			BufferedReader br = new BufferedReader(new InputStreamReader(is));
    			try{
    				decodeConf.getProps().load(br);
    				decodeConf.setRunDecode(Boolean.parseBoolean(decodeConf.getProps().getProperty("isRunDecode")));
    				decodeConf.setDirs(decodeConf.getProps().getProperty("dirs").split(","));
    				decodeConf.setPackages(decodeConf.getProps().getProperty("packages").split(","));
    				decodeConf.setClassPath(decodeConf.getProps().getProperty("classPath"));
    			}catch(Exception e){
    				e.printStackTrace();
    			}finally {
    				try {
    					br.close();
    					is.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    	
    	/**
    	 * 
    	 * @title: explicitPropertyFilePos
    	 * @description: 如果decode.properties不在类路径,调用此方法明确其位置
    	 * @author: 陈家宝
    	 * @version: V1.00
    	 * @date: 2019年3月12日 上午11:33:15
    	 * @param filepath
    	 */
    	public static void explicitPropertyFilePos(String filepath) {
    		try {
    			InputStream is = new FileInputStream(new File(filepath));
    			BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    			decodeConf = new DecodeConf();
    			decodeConf.getProps().load(reader);
    			decodeConf.setRunDecode(Boolean.parseBoolean(decodeConf.getProps().getProperty("isRunDecode")));
    			decodeConf.setDirs(decodeConf.getProps().getProperty("dirs").split(","));
    			decodeConf.setPackages(decodeConf.getProps().getProperty("packages").split(","));
    			decodeConf.setClassPath(decodeConf.getProps().getProperty("classPath"));
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	@Override
    	public String toString() {
    		return "classpath = " + decodeConf.classPath;
    	}
    }
    
    

     

    加/解密工具类:

    package com.jack.vesoft.classencrypt.main;
    
    import java.math.BigInteger;
    import java.security.MessageDigest;
    import java.security.SecureRandom;
    
    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.spec.SecretKeySpec;
    
    import com.jack.vesoft.encode.util.FileOperateHelper;
    import com.jack.vesoft.encode.util.Utils;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    /**
     * 
     * Title: EncodeUtil
     * Description: 加密工具类
     * @author 陈家宝
     * @date   2019年3月11日 下午1:46:15
     *
     */
    public class EncodeUtil {
    	
    	public static void main(String[] args) throws Exception {  
            /*String content = "admin";  
            System.out.println("加密前:" + content);  
      
            String key = "key";  
            System.out.println("加密密钥和解密密钥:" + key);  
              
            String encrypt = aesEncrypt(content, key);  
            System.out.println("加密后:" + encrypt);  
              
            String decrypt = aesDecrypt(encrypt, key);  
            System.out.println("解密后:" + decrypt); */ 
    		
    		String content = "w4rDvsK6wr4AAAAzAGIHAAIBADFjb20vc210L2lwdHYvcG9ydGFsL3dlYi9qc29uL0RhdGFDZW50L0RhdGFDZW50ZXJDb250cm9sbGVyBwAEAQAQamF2YS9sYW5nL09iamVjdAEAEmlwdHZwcm9maWxlU2VydmljZQEANExjb20vc210L2lwdHYvcG9ydGFsL3NlcnZpY2UvY21wL0lwdHZwcm9maWxlU2VydmljZTsBABlSdW50aW1lVmlzaWJsZUFubm90YXRpb25zAQA4TG9yZy9zcHJpbmdmcmFtZXdvcmsvYmVhbnMvZmFjdG9yeS9hbm5vdGF0aW9uL0F1dG93aXJlZDsBAAY8aW5pdD4BAAMoKVYBAARDb2RlCgADAA0MAAkACgEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBADNMY29tL3NtdC9pcHR2L3BvcnRhbC93ZWIvanNvbi9EYXRhQ2VudGVyQ29udHJvbGxlcjsBAA5nZXRJcHR2cHJvZmlsZQEAJChMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9MaXN0OwEACVNpZ25hdHVyZQEAOChMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9MaXN0PExqYXZhL2xhbmcvU3RyaW5nOz47AQA4TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2JpbmQvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZzsBAAV2YWx1ZQEADC9pcHR2cHJvZmlsZQEABm1ldGhvZAEAN0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZDsBAANHRVQBAAhwcm9kdWNlcwEAHmFwcGxpY2F0aW9uL2pzb247Y2hhcnNldD1VVEYtOAEANkxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9i";
    		System.out.println(FileOperateHelper.bytesToHexString(base64Decode(content)));
        }  
          
        /** 
         * 将byte[]转为各种进制的字符串 
         * @param bytes byte[] 
         * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 
         * @return 转换后的字符
         */  
        public static String binary(byte[] bytes, int radix){  
            return new BigInteger(1, bytes).toString(radix);// 
        }  
          
        /** 
         * base 64 encode 
         * @param bytes 待编码的byte[] 
         * @return 编码后的base 64 code 
         */  
        public static String base64Encode(byte[] bytes){  
            return new BASE64Encoder().encode(bytes);  
        }  
          
        /** 
         * base 64 decode 
         * @param base64Code 待解码的base 64 code 
         * @return 解码后的byte[] 
         * @throws Exception 
         */  
        public static byte[] base64Decode(String base64Code) throws Exception{  
            return Utils.isEmptyString(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);  
        }  
          
        /** 
         * 获取byte[]的md5
         * @param bytes byte[] 
         * @return md5 
         * @throws Exception 
         */  
        public static byte[] md5(byte[] bytes) throws Exception {  
            MessageDigest md = MessageDigest.getInstance("MD5");  
            md.update(bytes);  
              
            return md.digest();  
        }  
          
        /** 
         * 获取字符串md5
         * @param msg  
         * @return md5 
         * @throws Exception 
         */  
        public static byte[] md5(String msg) throws Exception {  
            return Utils.isEmptyString(msg) ? null : md5(msg.getBytes());  
        }  
          
        /** 
         * 结合base64实现md5加密 
         * @param msg 待加密字符串 
         * @return 获取md5后转为base64 
         * @throws Exception 
         */  
        public static String md5Encrypt(String msg) throws Exception{  
            return Utils.isEmptyString(msg) ? null : base64Encode(md5(msg));  
        }  
          
        /** 
         * AES加密 
         * @param content 待加密的内容 
         * @param encryptKey 加密密钥 
         * @return 加密后的byte[] 
         * @throws Exception 
         */  
        public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {  
            KeyGenerator kgen = KeyGenerator.getInstance("AES");  
            kgen.init(128, new SecureRandom(encryptKey.getBytes()));  
      
            Cipher cipher = Cipher.getInstance("AES");  
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));  
              
            return cipher.doFinal(content.getBytes("utf-8"));  
        }  
          
        /** 
         * AES加密为base 64 code 
         * @param content 待加密的内容 
         * @param encryptKey 加密密钥 
         * @return 加密后的base 64 code 
         * @throws Exception 
         */  
        public static String aesEncrypt(String content, String encryptKey) throws Exception {  
            return base64Encode(aesEncryptToBytes(content, encryptKey));  
        }  
          
        /** 
         * AES解密 
         * @param encryptBytes 待解密的byte[] 
         * @param decryptKey 解密密钥 
         * @return 解密后的String 
         * @throws Exception 
         */  
        public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {  
            KeyGenerator kgen = KeyGenerator.getInstance("AES");  
            kgen.init(128, new SecureRandom(decryptKey.getBytes()));  
              
            Cipher cipher = Cipher.getInstance("AES");  
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));  
            byte[] decryptBytes = cipher.doFinal(encryptBytes);  
              
            return new String(decryptBytes);  
        }  
          
        /** 
         * 将base 64 code AES解密 
         * @param encryptStr 待解密的base 64 code 
         * @param decryptKey 解密密钥 
         * @return 解密后的string 
         * @throws Exception 
         */  
        public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {  
            return Utils.isEmptyString(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);  
        }  
        
        /**
         * 
         * @title: myEncrypt
         * @description: 简单加密方式,每一位byte加一
         * @author: 陈家宝
         * @version: V1.00
         * @date: 2019年3月13日 上午1:51:56
         * @param buf
         * @return
         */
        public static byte[] simpleEncrypt(byte[] buf) {
        	if (buf == null || buf.length == 0) {
        		return null;
    		}
        	byte[] result = new byte[buf.length];
        	for(int i=0; i<buf.length; i++) {
        		result[i] = (byte) (buf[i] + 1);
        	}
        	
        	return result;
        }
        
        /**
         * 
         * @title: simpleDecrypt
         * @description: 简单解密方式,每一位byte减一
         * @author: 陈家宝
         * @version: V1.00
         * @date: 2019年3月13日 上午1:53:27
         * @param oldBytes
         * @return
         */
        public static byte[] simpleDecrypt(byte[] oldBytes) {
        	if (oldBytes == null || oldBytes.length == 0) {
        		return null;
    		}
        	byte[] result = new byte[oldBytes.length];
        	for(int i=0; i<oldBytes.length; i++) {
        		result[i] = (byte) (oldBytes[i] - 1);
        	}
        	
        	return result;
        }
    }
    
    
    

     方法虽然挺多的,但实际用到的就后面两个:simpleDecrypt(byte[] oldBytes)方法和simpleEncrypt(byte[] buf)方法。之前也是想采用什么base64、aes等算法对class文件内容进行加/解密。遗憾的是,没能做出来。因为使用那些算法涉及到了字符编码的问题,在读写class文件、new String()等细节上,虽然指定了编码,但最后发现文件内容还是不正确。主要是class文件的标志(MAGIC)出现了问题,文件后面的内容加解密正常。无奈之下,全程使用byte字节进行操作,不涉及字符编码,加/解密就成功实现了。

     

    另外一个解密工具类:

    package com.jack.vesoft.classencrypt.main;
    
    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    
    import com.jack.vesoft.encode.util.FileOperateHelper;
     
    /**
     * 
     * Title: StreamDecode
     * Description: 解密工具类
     * @author 陈家宝
     * @date   2019年3月11日 下午1:54:01
     *
     */
    public class StreamDecode {
     
    	public static InputStream decode(InputStream is) throws Exception{
    		byte[] oldBytes = FileOperateHelper.read(is);
    		byte[] newBytes = EncodeUtil.simpleDecrypt(oldBytes);
    		
    		// 解密得到的数据
    		String hexStr = FileOperateHelper.bytesToHexString(newBytes);
    		FileOperateHelper.log("StreamDecode->decode->hexStr=" + hexStr, true);
    		
    		return new ByteArrayInputStream(newBytes);
    	}
    	
    	public static void main(String[] args) throws Exception {
    		String pathname = "C:\\Develop\\apache-tomcat-7.0.93\\webapps\\iptvmanager_maven_test\\WEB-INF\\classes\\com\\smt\\iptv\\portal\\web\\json\\DataCenterController.class";
    		InputStream is = new FileInputStream(new File(pathname));
    		decode(is);
    	}
    }
    

     

     读写文件工具类:

    package com.jack.vesoft.encode.util;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Date;
    import java.util.List;
    
    /**
     * 
     * Title: FileOperateHelper
     * Description: 文件读写帮助类
     * @author 陈家宝
     * @date   2019年3月12日 下午1:37:01
     *
     */
    public class FileOperateHelper {
    	
    	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    	
    	private static final boolean HIDE_LOG = false;		// 隐藏调试输出
    	
    	/**
    	 * 
    	 * @title: read
    	 * @description: 读取文件内容
    	 * @author: 陈家宝
    	 * @version: V1.00
    	 * @date: 2019年3月13日 上午2:16:04
    	 * @param file
    	 * @return
    	 * @throws Exception
    	 */
    	public static byte[] read(File file) throws Exception {
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
    		byte[] buf = new byte[1024];
    		int len = 0;
    		List<Byte> list = new ArrayList<Byte>();
    		while((len = bis.read(buf)) > 0) {
    			for(int i=0; i<len; i++) {
    				list.add(buf[i]);
    			}
    		}
    		
    		bis.close();
    		
    		// InputStream.available()只是一个估计,不可靠,要不也没这么多事了
    		byte[] result = new byte[list.size()];
    		for(int i=0; i<list.size(); i++) {
    			result[i] = list.get(i);
    		}
    		return result;
    	}
    	
    	/**
    	 * 
    	 * @title: read
    	 * @description: 读取流内容
    	 * @author: 陈家宝
    	 * @version: V1.00
    	 * @date: 2019年3月13日 上午2:17:07
    	 * @param in
    	 * @return
    	 * @throws IOException
    	 */
    	public static byte[] read(InputStream in) throws IOException {
    		BufferedInputStream bis = new BufferedInputStream(in);
    		byte[] buf = new byte[1024];
    		int len = 0;
    		List<Byte> list = new ArrayList<Byte>();
    		while((len = bis.read(buf)) > 0) {
    			for(int i=0; i<len; i++) {
    				list.add(buf[i]);
    			}
    		}
    		
    		bis.close();
    		// InputStream.available()只是一个估计,不可靠,要不也没这么多事了
    		byte[] result = new byte[list.size()];
    		for(int i=0; i<list.size(); i++) {
    			result[i] = list.get(i);
    		}
    		return result;
    	}
    	
    	/**
    	 * 
    	 * @title: write
    	 * @description: 将内容写入文件
    	 * @author: 陈家宝
    	 * @version: V1.00
    	 * @date: 2019年3月13日 上午2:19:19
    	 * @param content
    	 * @param file
    	 * @throws Exception
    	 */
    	public static void write(byte[] content, File file) throws Exception {
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
    		bos.write(content);
    		bos.flush();
    		bos.close();
    	}
    	
    	/**
    	 * 
    	 * @title: log
    	 * @description: 做日志,不能断点调试太惨了
    	 * @author: 陈家宝
    	 * @version: V1.00
    	 * @date: 2019年3月12日 下午8:10:22
    	 * @param content 写入文件的内容
    	 * @param append 是否追加到文件末尾
    	 * @throws Exception
    	 */
    	public static void log(String content, boolean append) {
    		if (HIDE_LOG) {
    			return;
    		}
    		
    		try {
    			File file = new File("D:\\log.txt");
    			if (!file.exists()) {
    				file.createNewFile();
    			}
    			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,append)));
    			bw.write("\r\n" + sdf.format(new Date()) + "   " + content);
    			bw.flush();
    			bw.close();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		
    	}
    	
    	
    	
    	public static byte[] hexToBytes(String hex) {
    	    hex = hex.length() % 2 != 0 ? "0" + hex : hex;
    	 
    	    byte[] b = new byte[hex.length() / 2];
    	    for (int i = 0; i < b.length; i++) {
    	        int index = i * 2;
    	        int v = Integer.parseInt(hex.substring(index, index + 2), 16);
    	        b[i] = (byte) v;
    	    }
    	    return b;
    	}
    	
    	/**
         * Convert byte[] to hex string
         *
         * @param src byte[] data
         * @return hex string
         */
        public static String bytesToHexString(byte[] src){
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            return stringBuilder.toString();
        }
        
        public static void main(String[] args) {
    		byte[] b = hexToBytes("cafebabe");
    		String str = bytesToHexString(b);
    		System.out.println(str);
    	}
    }
    

     

     MyFileInputStream类,为了解决命令行编译修改的源码文件,getPath()获取不到的问题:

    package com.jack.vesoft.encode.util;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class MyFileInputStream extends FileInputStream {
    
    	public MyFileInputStream(File file) throws FileNotFoundException {
    		super(file);
    	}
    
    	@Override
    	public String getPath() {
    		// 命令行获取不到java.io.FileInputStream的getPath()方法,该方法是修改JDK而来。看下继承是否有效
    		// 事实证明,这么玩是可以的。我能怎么办呢?我也很绝望啊
    		return super.getPath();
    	}
    }
    

     

     手动对项目class文件进行加密的工具类:

    package test;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.InputStreamReader;
    
    import com.jack.vesoft.classencrypt.classreading.DecodeConf;
    import com.jack.vesoft.classencrypt.main.EncodeUtil;
    import com.jack.vesoft.encode.util.FileOperateHelper;
    
    public class QuickStart {
    	public static void main(String[] args) throws Exception {
    		// 命令行输入加/解密配置文件绝对路径
    		// C:\\Develop\\apache-tomcat-7.0.93\\lib\\decode.properties
    		System.out.println("请输入加/解密配置文件绝对路径:");
    		InputStreamReader isr = new InputStreamReader(System.in);
    		BufferedReader br = new BufferedReader(isr);
    		String filepath = br.readLine();
    		// 获取配置文件信息
    		DecodeConf.explicitPropertyFilePos(filepath);
    		
    		// 输出配置信息
    		String classpath = DecodeConf.getConf().getClassPath();
    		String[] dirs = DecodeConf.getConf().getDirs();
    		String[] pkgs = DecodeConf.getConf().getPackages();
    		
    		// 如果开启加密,循坏dirs路径数组,对要加密的路径下的文件递归进行加密
    		if (DecodeConf.getConf().isRunDecode()) {
    			encryptFileInTargetDirectory(dirs);
    		}
    	}
    	
    	/**
    	 * 
    	 * @title: encryptFileInTargetDirectory
    	 * @description: 递归进行加密
    	 * @author: 陈家宝
    	 * @version: V1.00
    	 * @date: 2019年3月12日 上午10:09:18
    	 * @param dirs
    	 * @throws Exception
    	 */
    	public static void encryptFileInTargetDirectory(String[] dirs) throws Exception {
    		for(int i=0; i<dirs.length; i++) {
    			String dir = dirs[i];
    			File file = new File(DecodeConf.getConf().getClassPath() + dir);
    			// 如果file为路径,递归
    			if (file.exists()) {
    				if (file.isDirectory()) {
    					encryptFiles(file.listFiles());
    				}else {
    					// 到这说明是文件,进行加密
    					encodeAndWriteBack(file);
    				}
    			}
    		}
    	}
    	
    	private static void encodeAndWriteBack(File file) throws Exception {
    		
    		
    		// 读取文件内容
    		byte[] oldcontent = FileOperateHelper.read(file);
    		// 进行加密
    		byte[] encodeString = EncodeUtil.simpleEncrypt(oldcontent);
    		// 将加密后的内容,覆盖原来的文件内容
    		FileOperateHelper.write(encodeString, file);
    		System.out.println(file.getPath() + " --> entrypt done!");
    	}
    
    	private static void encryptFiles(File[] listFiles) throws Exception {
    		for (File file : listFiles) {
    			if (file.isDirectory()) {
    				encryptFiles(file.listFiles());
    			}else {
    				// 到这说明是文件,进行加密
    				encodeAndWriteBack(file);
    			}
    		}
    		
    	}
    }
    

     将工具类所在的项目打成jar包后,用压缩包打开jar包,修改\META-INF\MANIFEST.MF文件,添加Main-Class: test.QuickStart指定运行jar时程序入口,修改后如下:

     注意:Main-Class: test.QuickStart(冒号后面有一个空格,整个文件最后有一行空行

    这样,就可以很方便的对项目的class文件进行加密了。

    在命令行运行指令:java -jar codeEncryptUtils.jar 完成加密。

    运行效果如下:

    这里示例对DataCenterController.class文件进行加密,没有加密前可以用jd-gui反编译:

     

    加密后:

     

     

     六、测试运行

    根据上述方法修改完成后把JDK、Tomcat、Spring等需要替换的jar备份一下,替换时选中jar包鼠标右键选择使用rar或360压缩工具打开jar包,然后根据“二、需要修改的类”中说明的对应位置进行替换,rar会提示压缩文件已修改是否需要保存,确定保存后就好了(注意:如果直接在JDK或Tomcat目录中打开替换可能会保存失败,需要将jar包复制到其他目录,比如D盘根目录什么的,再根据上面说的方式替换保存,最后将jar包覆盖回原来的地方),最后将加密好的类替换掉原来的类(注意实体类不能加密,否则在运行过程中会报错),把配置文件中isRunDecode设置为true,启动项目看下结果吧!!!

     

     七、局限性

    1、不能加密entity(实体类),因为实体类的字节码读取解析用到了JNI调用非JAVA程序完成,能力有限,这部分还没法修改。

    具体代码详见java.lang.ClassLoader类:

    2、不能加密@WebService类,理由同上。

     

    建议只对service和controller加密。

     

     八、总结

    做完下来,需要改动的地方不少,在摸索哪些地方需要修改以及如何修改着实花费了不少时间,甚至走了许多弯路,到目前为止感觉还是有很多不是很理解需要进一步研究的地方。如果你的项目用到了别的框架,可能需要改动别的地方,这里主要是提供一个思路,就不放项目链接了。如果有别的不错的方法防止反编译,则不建议这种方法,挺花时间的。

     

     

     

    展开全文
  • 1.创建类文件自动生成代码注释 让VS自动生成类的头部注释,需要修改安装目录下的一个 Class.cs文件,具体路径如下: E:\VS2015\Common7\IDE\ItemTemplatesCache\CSharp\Code 此目录下的均是代码模版,可以修改相应...
  • 心血来潮下载了SQLite源代码(版本 3.7.12.1),解压后只有4个文件:sqlite3.h,sqlite3ext.h,shell.c,sqlite3.c,简单粗暴我喜欢。 按照文档介绍,写了个最简单的demo程序如下: // The name of a database is ...
  • VC编程规范-程序员应该这样写代码 基本要求 1.1程序结构清析,简单易懂,单个函数的程序行数不得超过100行。 1.2打算干什么,要简单,直接了当,代码精简,避免垃圾程序。 1.3尽量使用标准库函数公共函数。 1.4...
  • 《VS2019使用clang-format实现源代码格式化排版》 https://libaineu2004.blog.csdn.net/article/details/112760035  --- 参考文献 https://www.jianshu.com/p/e8e0158258f0 clang-format常用配置 ...
  • 严格地,应该说,VC6或VC2005不能很好的支持对“行数超过65536的C/C++源代码文件”的跟踪调试。这是我(liigo)在准备参予为易语言开发最新版的sqlite3支持库的时候偶然发现的。从 SQLite 官方网站(sqlite.org)...
  • iOS代码规范

    千次阅读 2017-06-15 15:21:11
    通用规范:不局限于iOS的通用性的代码规范(使用C语言Swift语言)。 iOS规范:仅适用于iOS的代码规范(使用Objective-C语言)。 一. 核心原则原则一:代码应该简洁易懂,逻辑清晰因为软件是需要人来维护的。这个人...
  • (1)C++源代码扫描程序识别C++记号。 C++语言包含了几种类型的记号:标识符,关键字,数(包括整数、浮点数),字符串、注释、特殊符号(分界符)运算符号等。  (2)打开一个C++源文件,打印出所有以上的记号...
  • 但是匹配的行需要满足:非注释、非空等特殊非代码行。使用Ctrl+Shift+F快捷键打开高级查找,可以设置查找内容为正则表达式(需要在下面勾选,同时可以设置查找的文件类型),查找范围请根据需要进行设置。 点击“查找...
  •  源代码必须有确定的开始结尾代码段,不得有空行, 不得有注释 3. 页眉中软件名称、版本号应当与《计算机软件著作权申请表》完全一致(在左上角),右上角应标注页码 4. A4纸单面打印,黑白 5. 最好要有个封面 ...
  • VC 常用插件界面库

    千次阅读 2012-06-04 11:53:43
    VC 常用插件  1.Visual Assist(强烈推荐) [url]http://www.wholetomato.com/[/url] VA从5.0一直到现在的VAX,功能越来越强大,除了以前版本中的自动识别各种关键字,系统函数,成员变量,自动给出输入提示...
  • 星标/置顶公众号????,硬核文章第一时间送达!什么是好的程序员?是不是懂得很多技术细节?还是懂底层编程?还是编程速度比较快?我觉得都不是。对于一些技术细节来说底层的技术,只要看帮助,查资料...
  • C++:代码规范

    2019-09-28 10:35:09
    文件目录 在C/C++编程中,应该使用如下的文件名后缀: C++的头文件:.hpp(系统自动生成时允许.h,比如VC) C++的Inline函数实现头文件:_I.hpp C++的源文件:.cpp C的头文件:.h C的源文件:.c Perl编程中: Perl...
  • 星标/置顶公众号????,硬核文章第一时间送达!正文什么是好的程序员?是不是懂得很多技术细节?还是懂底层编程?还是编程速度比较快?我觉得都不是。对于一些技术细节来说底层的技术,只要看帮助,查...
  • iOS开发代码规范

    千次阅读 2017-08-29 11:25:35
    通用规范:不局限于iOS的通用性的代码规范(使用C语言Swift语言)。iOS规范:仅适用于iOS的代码规范(使用Objective-C语言)。一. 核心原则原则一:代码应该简洁易懂,逻辑清晰因为软件是需要人来维护的。这个人在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,731
精华内容 1,092
关键字:

vc源代码去掉注释和空行

友情链接: pyotoeal-Using.rar