美文网首页
基于JVMTI使用xxtea-c对JAVA字节码进行加密

基于JVMTI使用xxtea-c对JAVA字节码进行加密

作者: 淡淡的小番茄 | 来源:发表于2021-07-17 22:10 被阅读0次

    背景

    C++的加密库,基本都是依赖第三方,比如:rsa、des,GitHub上很多项目都是基于openssl来实现的。前面的有介绍过:我们最终选择了xxtea算法对字节码进行加密,主要是他简单高效,不依赖任何第三方。

    尝试了GitHub上的比价多的项目,最终发现xxtea-c项目能比较好的解决我们的问题。这个是C语言版本的xxtea算法实现。其间,多亏了此项目作者的给与我的c++代码的指点,最终才有了这篇文章的诞生。

    在windows下,我是直接通过cl命令来编译c、c++代码的,比较方便。

    新建C++工程

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

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

    编写BytecodeCrypto.cpp文件

    核心头文件如下:

    #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相关的头文件。

    核心加密逻辑如下:

    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;

    }

    核心解密逻辑如下:

    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文件。分别执行如下命令:

    cl /EHsc -LDd xxtea.c

    lib xxtea.obj /out:xxtea.lib

    编译C++代码

    cl /EHsc -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LDd BytecodeCrypto.cpp xxtea.lib

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

    编写加密JAVA类BytecodeCrypto.java

    代码如下:

    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

    java -agentpath:"**\BytecodeCrypto.dll" -jar Crypto_encrypted.jar

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

    相关文章

      网友评论

          本文标题:基于JVMTI使用xxtea-c对JAVA字节码进行加密

          本文链接:https://www.haomeiwen.com/subject/bfqgpltx.html