java class文件防止反编译

java项目发布war包,如何防止war包中class文件的反编译,以及静态文件的加密。
ps:有没有不需要输密码运行的,而是对文件本身进行防止反编译,其他的程序都是正常流程启动运行

加密
混淆

使用classfinal之类的工具进行加密混淆即可

有这四种思路供你参考:
1.隔离Java程序
2.对Class文件进行加密   
3.转换成本地代码  
4.代码混淆

classfinal 比较方便易用。唯一缺点就是命令行。所以我对它做了一个界面封装。
题主有兴趣的话,私我吧

xjar jar包加密

Java防止反编译
非常详细
https://blog.csdn.net/weixin_39286679/article/details/125329652

​关于你说的问题其实是软件保护,你可以看我写的一篇博客,专门介绍相关内容,由于是vip博客,我将大部分内容粘贴于此,供参考

软件保护方案

背景

Java是一种跨平台的解释型语言,由于跨平台的需求,Java的指令集比较简单而通用,较容易得出程序的语义信息。因此java开发的软件在诸如安装到客户端的商业环境下,往往存在商业机密泄露,滥用的风险,对企业生产者造成不可估量的损失。

Java编译器将每一个类编译成一个单独的文件,这也简化了反编译的工作。Java源代码编译中间“字节码”存储于Class文件中。Class文件是一种字节码形式的中间代码,该字节码中仍然保留所有的方法名称、变量名称,并且通过这些名称来访问变量和方法,这些符号往往带有许多语义信息。由于Java程序自身的特点,对于不经过处理的Java程序,反编译的效果非常好。

目前,市场上有许多Java的反编译工具,这些工具的反编译速度和效果都很不错。好的反编译软件,能够反编译出非常接近源代码的程序。通过反编译器,黑客能够对这些程序进行更改,或者复用其中的程序。因此,如何保护Java程序不被反编译,是非常重要的一个问题。此外,Java程序基于JVM这一“中间层”能够在不同平台上运行,真正实现了“一次编译,到处运行”的目的。Java的这一特性对商业软件的版权保护也带来了挑战。

因此java开发的软件在诸如安装到客户端的商业环境下,往往存在商业机密泄露,滥用的风险,对企业生产者造成不可估量的损失。为此,本文就围绕维护生产者合法权益,商业机密的保护,即软件保护方案作出若干讨论。

软件保护主要涉及两大方面

源文件保护

目前针对Java源文件方法主要有本地编译、数字水印、ClassLoader加密,以及代码混淆。

1.本地编译

Java本地编译是指将Java应用程序编译成本地应用程序,如 Windows平台下名为exe的应用程序。

通过java虚拟机将源代码生成Java类文件,再将类文件编译成可执行文件。用该技术生成的本地应用程序是二进制格式的可执行文件,但该方法牺牲了java的跨平台特性,对于桌面应用程序的开发问题不大但是对于Web应用程序的开发,则是一个致命缺陷。

2.数字水印

数字水印技术是将一些标识信息(即数字水印)直接嵌入数字载体(包括多媒体、文档、软件等)当中但不影响原载体的使用价值,也不容易被人的知觉系统觉察或注意到。通过这些隐藏在载体中的信息,可以达到确认内容创建者、购买者、传送隐秘信息或者判断载体是否被篡改等目的。在需要证明程序是否非法使用时,数字水印就变得很重要。使用水印技术并不能阻止类文件被反编译,但是可以在需要确认某些程序是否属于剽窃时提供有效证据。

实施方案:

第一步、创建印章库

添加的数字水印不能破坏原来代码的运行,而注释打包后会清除,因此以注解方式隔行嵌入。由于反编译后,水印可以通过正则,批量删除,因此添加的水印需要具备不可批量替换性,与一般代码注解相仿的仿真性。

我们需要建立印章库,即存储所有生成水印所需的印章来生成水印,并且可以校验反编译后的代码是否包含我们独有的水印来作为代码剽窃的证据。

通过find . -name ".js" -or -name ".tpl" -or -name ".xml" -or -name ".java" -or -name "*.css" |xargs grep -v " ^$"|wc

-l

统计现有项目roshi-parent(54445行),recruit-parent(109426行)代码知,印章库需要100000印章。

第二步、生成印章

可以通过随机生成长度在10~100的纯字母字符串做为印章。

例如:Afefefefefefe

第三步、添加水印

需要创建添加水印服务,首先从印章库随机获取印章,假如为Afefefefefefe。

根据已经获取到的印章生成印章注解,放在com.roshi.print的包下。

印章代码如图所示:

逐行扫描源代码,在所有public修饰行的前方增加水印@Afefefefefefe,每添加一次水印就根据印章库的印章重新生成一次印章,尽可能让水印不同。

添加完水印的代码如图所示:

3. Classloader加密

Classloader的基本目标是对类的请求提供服务。

当JVM需要使用类时,它根据名称向 Classloader请求这个类,然后 Classloader试图返回一个表示这个类的Class对象。通过覆盖对应于这个过程不同阶段的方法,可以创建定制的 Classloader。在装入原始数据后先进行解密,再转换成 Class对象。由于把原始字节码转换成Class对象的过程完全由系统负责,因此只需先获得原始数据,接着就可以进行包含解密在内的任何转换。这种方案比其他方案更加安全,然而这种加密方法存在一个漏洞,由于 Classloader的类是用Java编写的,如果对 Classloader类进行反编译,提取其中解密算法,就可解密所有被加密的其他类。

实施方案

====》自己重写classloader


public class MyClassLoader extends ClassLoader{


    public static void main(String[] args) throws IOException {

        String srcPath = args[0];
        String destDir = args[1];

        String destFileName = srcPath.substring(srcPath.lastIndexOf("\\") + 1);
        String destFilePath = destDir + "\\" + destFileName;

        FileInputStream fin = new FileInputStream(srcPath);
        FileOutputStream fout = new FileOutputStream(destFilePath);

        cypher(fin,fout);

        fin.close();
        fout.close();
    }

    /**
     * <p>Title: cypher</p>
     * <p>Description:加密, 将原来的1改为0,0改为1</p>
     * @param in
     * @param out
     * @throws IOException
     */
    public static void cypher(InputStream in ,OutputStream out) throws IOException{

        int b=0;
        while ((b=in.read())!=-1) {
            out.write(b ^ 0xff);
        }
    }

    private String classDir;

    public MyClassLoader(){

    }
    public MyClassLoader(String classDir){
        this.classDir=classDir;
    }

    /**
     * 覆盖ClassLoader的findClass方法
     */
    @SuppressWarnings("deprecation")
    @Override
    protected Class<?> findClass(String name)
            throws ClassNotFoundException {

        String classFileName = classDir + "\\" + name + ".class";
        try {
            FileInputStream fin = new FileInputStream(classFileName);
            ByteArrayOutputStream baout = new ByteArrayOutputStream();
            //解密
            cypher(fin,baout);
            fin.close();
            //转为字节数组
            byte[] byteArray = baout.toByteArray();
            return defineClass(byteArray, 0, byteArray.length);

        } catch (Exception e) {
        }

        return null;
    }

}




运行MyclassLoader的main方法,得到加密后的class,加密后的class再通过以下测试代码进行解密。

@SuppressWarnings("rawtypes")
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {


    Class c = new MyClassLoader("myloaderlib").loadClass("ClassLoaderAttach");
    Date d = (Date)c.newInstance();
    System.out.println(d);

}

====》通过xjar

xjar是什么

xjar的定义

Spring Boot JAR 安全加密运行工具,同时支持的原生JAR。

基于对JAR包内资源的加密以及拓展ClassLoader来构建的一套程序加密启动,动态解密运行的方案,避免源码泄露或反编译

功能特性

无需侵入代码,只需要把编译好的JAR包通过工具加密即可。
完全内存解密,杜绝源码以及字节码泄露或反编译。
支持所有JDK内置加解密算法。
可选择需要加解密的字节码或其他资源文件,避免计算资源浪费。
第一步、导入依赖

<dependencies>
        <!--核心库-->
        <dependency>
            <groupId>com.github.core-lib</groupId>
            <artifactId>xjar</artifactId>
            <version>v2.0.6</version>
        </dependency>
        <!--用于读取jar中的文件-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.18</version>
        </dependency>
        <!--资源加载器-->
        <dependency>
            <groupId>com.github.core-lib</groupId>
            <artifactId>loadkit</artifactId>
            <version>v1.0.0</version>
        </dependency>
</dependencies>

第二步、加密普通jar

public static void main(String[] args) throws Exception {
        String password = args[0];
        XKey xKey = XKit.key(password);
        XJar.encrypt(args[1], args[2], xKey);
        System.out.println("Successfully generated encrypted jar");
}

第三步、解密普通jar

public static void main(String[] args) throws Exception {
        String password = args[0];
        XKey xKey = XKit.key(password);
        XJar.decrypt(args[1], args[2], xKey);
}

启动方式

(1)启动后提示输入密码

java jar auth.jar

(2)通过传参方式启动


java jar auth.jar --xjar.password=123456

linux上推荐使用nohup后台启动


nohup java jar auth.jar --xjar.keyfile=xjar.key

注:xjar.key可配置password项

由于本机在windows环境调试,下图为windows环境调试结果:

(1)输入java –jar auth.jar

(2)输入密码后

(3)未加密

(4)已加密

注意:

启动时也可以不需要密码,可危险加密模式,避免输入密码
// 危险加密模式,即不需要输入密码即可启动的加密方式,这种方式META-INF/MANIFEST.MF中会保留密钥,请谨慎使用!

String password = "io.xjar";

XKey xKey = XKit.key(password);

XBoot.encrypt("/path/to/read/plaintext.jar", "/path/to/save/encrypted.jar", xKey, XConstants.MODE_DANGER);

现在xjar官方已经移除了危险模式,可能是该模式没有存在的意义所以移除了。

 

(2)Maven项目可通过集成 xjar-maven-plugin 以免去每次加密都要执行一次上述的代码, 随着Maven构建自动生成加密后的JAR和Go启动器源码文件.

xjar-maven-plugin GitHub: https://github.com/core-lib/xjar-maven-plugin


<project>
    <!-- 设置 jitpack.io 插件仓库 -->
    <pluginRepositories>
        <pluginRepository>
            <id>jitpack.io</id>
            <url>https://jitpack.io</url>
        </pluginRepository>
    </pluginRepositories>
    <!-- 添加 XJar Maven 插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>com.github.core-lib</groupId>
                <artifactId>xjar-maven-plugin</artifactId>
                <version>4.0.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                        </goals>
                        <phase>package</phase>
                        <!-- 或使用
                        <phase>install</phase>
                        -->
                        <configuration>
                            <password>${xjar.password}</password>
                            <!-- optional
                            <algorithm/>
                            <keySize/>
                            <ivSize/>
                            <includes>
                                <include/>
                            </includes>
                            <excludes>
                                <exclude/>
                            </excludes>
                            <sourceDir/>
                            <sourceJar/>
                            <targetDir/>
                            <targetJar/>
                            -->
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

对于Spring Boot 项目或模块, 该插件要后于 spring-boot-maven-plugin 插件执行, 有两种方式:

将插件放置于 spring-boot-maven-plugin 的后面, 因为其插件的默认 phase 也是 package

将插件的 phase 设置为 install(默认值为:package), 打包命令采用 mvn clean install

也可以通过Maven命令执行

mvn xjar:build -Dxjar.password=io.xjar

mvn xjar:build -Dxjar.password=io.xjar -Dxjar.targetDir=/directory/to/save/target.xjar

但通常情况下是让XJar插件绑定到指定的phase中自动执行, 这样就能在项目构建的时候自动构建出加密的包.

mvn clean package -Dxjar.password=io.xjar

·加密成功后XJar会在输出的JAR包同目录下生成一个名为 xjar.go 的Go启动器源码文件.

·将 xjar.go 在不同的平台进行编译即可得到不同平台的启动器可执行文件, 其中Windows下文件名为 xjar.exe 而Linux下为 xjar。

·用于编译的机器需要安装 Go 环境, 用于运行的机器则可不必安装 Go 环境, 具体安装教程请自行搜索。

由于启动器自带JAR包防篡改校验, 故启动器无法通用, 即便密码相同也不行。

·每次生成的go启动器只能启动对应的加密jar,无法启动其他的加密jar。

4.代码混淆

实施方案

第一步、修改pom增加插件配置


<build>
        <plugins>
            <!-- ProGuard混淆插件-->
            <plugin>
                <groupId>com.github.wvengen</groupId>
                <artifactId>proguard-maven-plugin</artifactId>
                <version>2.5.1</version>
                <executions>
                    <execution>
                        <!-- 混淆时刻,这里是打包的时候混淆-->
                        <phase>package</phase>
                        <goals>
                            <!-- 使用插件的什么功能,当然是混淆-->
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- 是否将生成的PG文件安装部署-->
                    <attach>true</attach>
                    <!-- 是否混淆-->
                    <obfuscate>true</obfuscate>
                    <!-- 指定生成文件分类 -->
                    <attachArtifactClassifier>pg</attachArtifactClassifier>
                    <options>
                        <!-- JDK目标版本1.8-->
                        <option>-target 1.8</option>
                        <!-- 不做收缩(删除注释、未被引用代码)-->
                        <option>-dontshrink</option>
                        <!-- 不做优化(变更代码实现逻辑)-->
                        <option>-dontoptimize</option>
                        <!-- 不路过非公用类文件及成员-->
                        <option>-dontskipnonpubliclibraryclasses</option>
                        <option>-dontskipnonpubliclibraryclassmembers</option>
                        <!--不用大小写混合类名机制-->
                        <option>-dontusemixedcaseclassnames</option>

                        <!-- 优化时允许访问并修改有修饰符的类和类的成员 -->
                        <option>-allowaccessmodification</option>
                        <!-- 确定统一的混淆类的成员名称来增加混淆-->
                        <option>-useuniqueclassmembernames</option>
                        <!-- 不混淆所有包名-->
                        <!--<option>-keeppackagenames</option>-->

                        <!-- 需要保持的属性:异常,注解等-->
                        <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod</option>
                        <!-- 不混淆所有的set/get方法-->
                        <!-- <option>-keepclassmembers public class * {void set*(***);*** get*();}</option>-->

                        <!-- 不混淆包下的所有类名,且类中的方法也不混淆-->
                        <option>-keep class com.xxx.xxx.framework.** { *; }</option>
<!--                        <option>-keep class com.xxx.xxx.xxx.controller.** { *; }</option>-->
<!--                        <option>-keep class com.xxx.xxx.xxx.dao.** { *; }</option>-->
<!--                        <option>-keep class com.xxx.xxx.xxx.model.** { *; }</option>-->

                    </options>
                    <!--class 混淆后输出的jar包-->
                    <outjar>classes-autotest.jar</outjar>
                    <!-- 添加依赖,这里你可以按你的需要修改,这里测试只需要一个JRE的Runtime包就行了 -->
                    <libs>
                        <lib>${java.home}/lib/rt.jar</lib>
                        <lib>${java.home}/lib/jce.jar</lib>
                    </libs>
                    <!-- 对什么东西进行加载,这里仅有classes成功,毕竟你也不可能对配置文件及JSP混淆吧-->
                    <injar>classes</injar>
                    <!-- 输出目录-->
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

第二步、执行mvn clean package生成经过代码混淆的包,如图所示

软件授权

软件授权难点
激活码需要和硬件信息相结合。硬件信息是唯一且不变的,做到“一机一码”,保证每台机器的激活码不可通用,单独授权,并且要做到硬件信息不易推测出来。
安全的授权校验机制,解密的过程是一个明文信息暴露的过程,如果校验机制出现了隐患,对软件来说则是致命的威胁。
生成激活码的算法的安全性。基于软加密的保护机制的核心在于加解密算法,采用安全可靠的加解密算法防止软件轻易被破坏。
具有一定的自身检查能力,包括对磁盘文件和内存映像的检查,避免软件在开发者不知情的情况下修改校验机制,被破解而不自知。
软件授权步骤
首先,客户启动软件,软件提取能够唯一标识机器身份的指纹信息,使用加密算法对其加密形成注册码。服务端收到注册码之后,根据软件的基本信息和用

户的需求信息(功能需求和使用期限)生成激活码,发送给客户端。客户端使用激活码激活软件,在校验激活信息无误后,进入软件主界面。

三、关键步骤

1、注册码生成

计算机中,可以作为指纹的能读取的硬件信息有:MAC地址,CPU序列号,BOS序列号,硬盘序列号。

硬件信息

特点

是否可行

MAC地址

具有全球唯一性;

V

CPU序列号

如果是同一批次同一配置的电脑,CPU序列号存在重复的可能;

X

BOS序列号

BOS序列号的读取并不容易,但有些主板没有BOS序列号,使用些工具也可以轻易更改它,如 DMIScope;

X

硬盘序列号

硬盘序列号是独一无二的,是计算机信息的重要组成部分,如果执意修改,可能会导致系统错误,硬盘无法使用

V

所以本方案采取读取硬盘序列号和MAC地址作为指纹信息。采用这一方式有一定的风险,两种信息的简单组合很容易被推测出所使用的硬件信息,在安全性上有所欠缺。

如果不法分子拿到推测出的硬件信息,如果我们的软件安装在客户端,那么不法分子完全可以通过反编译,修改源代码的方式,模拟付费客户的硬盘序列号和mac地址,就可以打破一机一码的限制。

故本方案中将硬盘序列号和MAC地址分组进行交叉,在进行加密,使信息变得复杂化,以减少加密解密过程的明文暴露。

密钥生成的变换公式如下所示:

I=F(MAC地址,硬盘序列号)

F为交叉变换函数,变换过程如图所示

使用散列函数对软件运行机器的惟一标识信息进行数学运算,生成符合加密算法要求的密钥 K。 MD5即Message-Digest Algorithm 5(信息-摘要算法 5),是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法)。MD5的作用是以任意长度的信息作为输入进行计算,产生一个128-bit(16-byte)的指纹或报文摘要。对惟一标识 I进行 MD5 计算后得到一个 128位摘要信息,作为加密算法的密钥K。

由于硬件信息的长度不固定,注册码的生成采用摘要算法,生成固定长度的消息摘要。MD5和SHA算法根据相关研究结果,不再安全,所以采用安全性更高的SM摘要算法生成摘要。将其作为加密算法的消息输人,摘要算法加密的不可逆性,可以保证硬件信息不被反推出来。

Mac地址,磁盘序列号获取:

(1)判断操作系统是Windows还是Linux

private static Boolean isLinux() {
String os = System.getProperty("os.name");
return !os.toLowerCase().startsWith("win");
}

(2)Linux

获取MAC地址

private static String getMACAddressByLinux() throws Exception {
String[] cmd = {"ifconfig"};

Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();

BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
    sb.append(line);
}

String str1 = sb.toString();
String str2 = str1.split("ether")[1].trim();
String result = str2.split("txqueuelen")[0].trim();
System.out.println("Linux MacAddress is: {}" + result);
br.close();

return result;

}

获取硬盘序列号

private static String getIdentifierByLinux() throws Exception {
String[] cmd = {"fdisk", "-l"};

Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();

BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
    sb.append(line);
}

String str1 = sb.toString();
String str2 = str1.split("identifier:")[1].trim();
String result = str2.split("Device Boot")[0].trim();
System.out.println("Linux Identifier is: {}"+result);
br.close();

return result;

}

(3)Windows:

获取MAC地址: (默认获取第一张网卡)

private static String getMACAddressByWindows() throws Exception {
String result = "";
Process process = Runtime.getRuntime().exec("ipconfig /all");
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));

String line;
int index = -1;
while ((line = br.readLine()) != null) {
    index = line.toLowerCase().indexOf("物理地址");
    if (index >= 0) {// 找到了
        index = line.indexOf(":");
        if (index >= 0) {
            result = line.substring(index + 1).trim();
        }
        break;
    }
}
System.out.println("Windows MACAddress is: {}"+result);
br.close();
return result;

}

获取硬盘序列号: (默认获取C盘)

private static String getIdentifierByWindows() throws Exception {
String result = "";
Process process = Runtime.getRuntime().exec("cmd /c dir C:");
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));

String line;
while ((line = br.readLine()) != null) {
    if (line.indexOf("卷的序列号是 ") != -1) {
        result = line.substring(line.indexOf("卷的序列号是 ") + "卷的序列号是 ".length(), line.length());
        break;
    }
}
System.out.println("Windows Identifier is: {}"+result);
br.close();
return result;

}

(4)测试:

public static void main(String[] a) throws Exception {
// 判断是Linux还是Windows
if (isLinux()) {
// Linux操作系统
String macAddress = getMACAddressByLinux();
System.out.println("Linux macAddress: " + macAddress);
String Identifier = getIdentifierByLinux();
System.out.println("Linux Identifier: " + Identifier);
} else {
// Windows操作系统
String macAddress = getMACAddressByWindows();
System.out.println("Windows macAddress: " + macAddress);
String Identifier = getIdentifierByWindows();
System.out.println("Windows Identifier: " + Identifier);
}
}



2、加密解密算法的选择

非对称加密算法相比,对称密码算法运算速度快,加密效率高,不需要考虑安全传输的问题。

在对称密码算法中,国产SM4对称分组密码算法具有较高的可靠性和安全性,故本方案中采用SM4算法。它的分组长度为128bit,密钥长度也为等长的128bit。加密算法和密钥扩展算法均采用32轮非线性迭代结构,每次迭代为一个圆形变换函数f。解密变换与加密变换结构相同,不同的仅是轮密钥的使用顺序

加密时轮密钥的使用顺序为:(rk0,rk1,…,rk31)

解密时轮密钥的使用顺序为:(rk31,k30,…,rk0)

SM4算法背景

2021年6月25日,我国SM4分组密码算法作为国际标准ISO/IEC 18033-3:2010/AMD1:2021《信息技术 安全技术 加密算法 第3部分:分组密码 补篇1:SM4》,由国际标准化组织ISO/IEC正式发布。

是继SM2/SM9数字签名算法、SM3密码杂凑算法、祖冲之密码算法和SM9标识加密算法之后,我国又一个商用密码算法被纳入ISO/IEC国际标准正式发布,标志着我国商用密码算法国际标准体系进一步成熟,展现了我国先进的密码科技水平和国际标准化能力,对提升我国商用密码产业发展、推动商用密码更好服务“一带一路”建设具有重要意义。

软件如何激活
(1)软件供应商服务端获取到注册码后,根据客户硬件信息生成激活申请,软件供应商服务端审核通过后,生成激活码(如果没有审核系统,可以先通过邮件申请激活码,需要先生成注册码,生成注册码可以提供一个jar包,在客户服务器生成后,再通过邮件发送过来)。

(2)激活码由软件供应商提供生成。SM4算法为分组为128lit长度的对称加密算法,加密数据格式较为固定,先验证注册码的合法性,如果合法(主要验证是否为软件供应商的客户机器),把授权信息,如使用期限、使用功能等,将传递过来的注册码作为密钥,进行加密,生成最终的激活码。

(3)客户端获取到激活码后,首先和生成过程类似,生成解析授权信息的软件密钥使用软件密钥对激活码解密,获取授权信息,判断授权信息的数据格式是否合法(是否包含授权信息应该具备的信息)。这一步也是对激活码进行校验,如果数据格式不合法说明激活码中加密的注册码有误,无法对软件激活。获取到软件信息后,判断软件的使用期限和功能信息是否有效,有效则进入软件主界面。

4、到期时间检测

在软件启动时,获取激活码的有效期信息进行时间校验。读取注册表(linux可以存在/home/用户名文件夹内)的上次使用时间以及当前系统时间,如果当前系统时间在上次使用时间之后,且判断当前系统时间是否在有效期内,如果不是则退出软件。如果在软件运行中修改时间,程序检测到当前时间小于上次使用时间,则停止运行,直到时间修改回来才能继续运行,上次使用时间每次校验都随之更新。每次软件退出时更新上次使用时间,保存时对时间通过对称加密算法SM4进行加密。

5、功能模块信息如何校验

在软件运行期间,调取相关接口前会获取内存内的授权信息,在客户端软件中嵌入功能模块信息列表,用户使用某一功能模块的时候,检测程序中的功能模块码值和授权信息中的功能信息是否一致,则开放对应模块的功能。

防止恶意篡改
为了阻止恶意程序修改软件信息,增加了对软件的完整性校验。对原始文件使用SM3计算得到散列值,或者是hash算法计算出hash值,并将值放在某处(比如注册表),每当class文件开始运行时,重新计算文件的散列值,同原值进行比较,如果不同则表明文件已经被修改。如果检测到数据被修改,则停止程序的运行。服务每次启动时检查即可。

比如:

Java 调用 System.exit(0)

实施步骤

通过判断jar包的sha-1与MD5的hash值是否发生改变来判断jar是否经过篡改

关于软件违规,我方进行强制关闭的操作

1.如果对方将软件运行在外网

我们可以在软件启动时开启监控定时任务,通过该任务将软件运行期间产生的一些我们希望收集的必要数据,打包,加密后发送给我们的服务器。如果我们需要强制关闭对方的软件,那么我们可以在响应处加入关闭指令。

2.如果对方将软件运行在内网

我们需要设置软件到期时间,可以是一个月,两个月,甚至一年。如果软件到期,可以在线进行激活,或者我们重新提供一个激活码。不论是如何激活,我们都可以对客户行为审核,审核通过,我们可以在指定时间内再次激活客户端软件。如果审核不通过,我们强制关闭软件。