JAVA Class文件加密小探

背景

我们基于TrueLicense实现了license的校验。这块网上资料很多,在此就不加以介绍了。今天主要想说一说机器指纹的问题。

主要包括如下两部分:

1、指纹的采集生成

2、指纹的校验

我们所做的一切都是防君子不防小人,任何东东都有破解的方式方法,能做的只是增加其难度。项目中的开发语言使用的是java,由于java的跨平台特性,jvm加载执行的是字节码,字节码有一套规范,很容易进行反编译。基于此,我们做了如下工作:

指纹采集

使用go语言采集机器指纹信息,机器指纹是线下采集,采集的信息需要进行加密,我们使用的是的AES对称加密。目前支持:

/*****************************

*arm架构,Linux version 4.19.90-9.ky10.aarch64 (YHKYLIN-OS@kernel-rpmbuild-amd64) (gcc version 7.3.0 (GCC)) #1 SMP Sun Apr 26 11:05:59 CST 2020

*amd64架构,CentOS Linux release 7.2.1511 (Core)

******************************/

采集上来的密文,通过解密工具解密获取到明文,然后作为license的扩展参数加密存储(sm2)。

对Licencse管理类进行加密

基于JVM Tool Interface对加密的类进行解密

加密和解密是对应的。由于C或者C++加解密基本都需要依赖第三方软件,其间,我们尝试了基于openssl的rsa、des加密,结果都不仅如人意,主要还是依赖太多,我们的需求很简单,想使用一个简单且不依赖第三方库的算法,于是我们打算使用tea算法,简单高效。然后选择了xxtea-c尝试对class文件进行加密。

新建C++工程

xxtea-c github地址:github.com/xxtea/xxtea...

将xxtea.c、xxtea.h拷贝到BytecodeCrypto cpp工程下,我在windows下使用的是Microsoft Visual Studio Community 2019。安装好后,配置好环境变量就可以使用cl命令了。

编写BytecodeCrypto.cpp文件

头文件:

arduino 复制代码
#include "BytecodeCrypto.h
"#include "xxtea.h"
#include <jni.h>
#include <jvmti.h>
#include <jni_md.h>

BytecodeCrypto.h里面主要定义了加密方法,后面供java调用。xxtea.h为xxtea-c工程的头文件,主要定义了xtea的加解密方法。jni、jvmti、jni_md为jvm相关的头文件。

核心加密逻辑:

scss 复制代码
Java_cn_cuiot_dmp_util_BytecodeCrypto_encrypt(JNIEnv * env, jclass cla, jbyteArray j_array){ 
       char* dst = (char*)env->GetByteArrayElements(j_array, 0);        
       int len_arr = env->GetArrayLength(j_array);        
       size_t len;        
       unsigned char* encrypt_data = (unsigned char*)xxtea_encrypt(dst, len_arr, XXTEA_SECRET, &len);        
       jbyteArray c_result = env->NewByteArray(len);        
       env->SetByteArrayRegion(c_result, 0, len, (jbyte*)encrypt_data);        
       return c_result;
}

核心解密逻辑:

ini 复制代码
void JNICALL BytecodeCryptoHook(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jclass class_being_redefined,jobject loader,const char* name,jobject protection_domain,jint class_data_len,const unsigned char* class_data,jint* new_class_data_len,unsigned char** new_class_data){
        if (name && strncmp(name, "cn/cuiot/dmp/crypto", 19) == 0) {
                  size_t len;                  
                  unsigned char* decrypt_data = (unsigned char*)xxtea_decrypt(class_data, class_data_len, XXTEA_SECRET, &len);                  
                  *new_class_data_len = len;                  
                  jvmti_env->Allocate(len, new_class_data);                  
                  unsigned char* _data = *new_class_data;                 
                  for (int i = 0; i < class_data_len; i++){                       
                   _data[i] = decrypt_data[i];                  
                  }        
        }else {                 
                  *new_class_data_len = class_data_len;                  
                  jvmti_env->Allocate(class_data_len, new_class_data);                 
                  unsigned char* _data = *new_class_data;                 
                  for (int i = 0; i < class_data_len; i++){
                         _data[i] = class_data[i];                  
                  }                
         }
}

生成xxtea lib文件

由于xxtea-c是c代码,我们主程序是c++代码,所以事先需要生成lib文件。分别执行如下命令:

bash 复制代码
cl /EHsc -LDd xxtea.clib xxtea.obj /out:xxtea.lib

编译C++代码

perl 复制代码
cl /EHsc -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LDd BytecodeCrypto.cpp xxtea.lib

生产的dll文件就是我们需要的。

编写加密JAVA类BytecodeCrypto.java

ini 复制代码
public class BytecodeCrypto {
   static {
         System.load("D:\\mf\\BytecodeCrypto\\BytecodeCrypto\\BytecodeCrypto\\BytecodeCrypto.dll");
   }
    public native static byte[] encrypt(byte[] text);    
    public static void main(String[] args) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            String fileName = "D:\\mf\\Crypto.jar";
            File srcFile = new File(fileName);
            File dstFile = new File(fileName.substring(0, fileName.indexOf(".")) + "_encrypted.jar");
            FileOutputStream dstFos = new FileOutputStream(dstFile);JarOutputStream dstJar = new JarOutputStream(dstFos);
            JarFile srcJar = new JarFile(srcFile);
            for (Enumeration<JarEntry> enumeration = srcJar.entries(); enumeration.hasMoreElements();) {
                JarEntry entry = enumeration.nextElement();
                InputStream is = srcJar.getInputStream(entry);
                int len;
                while ((len = is.read(buf, 0, buf.length)) != -1) {
                    baos.write(buf, 0, len);
                }
                byte[] bytes = baos.toByteArray();
                String name = entry.getName();
                if (name.startsWith("cn/xxx/yyy/crypto/Crypto.class")) {
                    try {
                            bytes = BytecodeCrypto.encrypt(bytes);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                }
                JarEntry ne = new JarEntry(name);
                dstJar.putNextEntry(ne);
                dstJar.write(bytes);
                baos.reset();
                }
                srcJar.close();
                dstJar.close();
                dstFos.close();
                System.out.println("encrypt finished");
        } catch (Exception e) {
        e.printStackTrace();
}
}
}

配置好dll文件路径和加密前jar路径。执行main方法,会输出加密后的jar包:

Crypto_encrypted.jar,你可以使用反编译工具查看下是否已加密成功。

执行加密jar

arduino 复制代码
java -agentpath:"**\BytecodeCrypto.dll" -jar Crypto_encrypted.jar

如果一切顺利的话,将会执行main class的main方法。

相关推荐
四谎真好看1 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程1 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t1 小时前
ZIP工具类
java·zip
lang201509282 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan2 小时前
第10章 Maven
java·maven
百锦再3 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说3 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多3 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
百锦再3 小时前
对前后端分离与前后端不分离(通常指服务端渲染)的架构进行全方位的对比分析
java·开发语言·python·架构·eclipse·php·maven
DokiDoki之父4 小时前
Spring—注解开发
java·后端·spring