1.使用程序对.class文件进行加密,之后使用jvm hook对加载的.class文件进行解密然后完成类加载的工作
2.注意事项
由于java解密代码调用非常的频繁 所以一定要注意申请的资源要及时释放 否则会导致内存泄漏以至程序崩溃
目前该程序只适用于java 1.6 java1.5实现未作测试 java1.7不能使用 由于底层实现有变java1.7的兼容性开发还未做
3.程序解释
1)加密程序
可采用对称加密如需要可以再修改成其它加密算法 并对加密的文件做上加密标记
2)
1>.Java agent程序 java虚拟机在运行是使用-agentpath或-agentlib参数加载该dll 如
java -agentpath:C:\temp\xxxagent.dll=C:\temp\key.dat HelloWorld
其中的HelloWorld.class为已经加密过的类 当然如果这个类未加密也可以正常运行 xxxagent.dll会识别哪些类加密了
2>.该dll向jvm注册了一些本地的方法最重要的一个是defineClass1
3>.注册之后在做了解密处理之后还要调用jvm本身的函数defineClass1(这个函数位于java.dll中)
4>.此程序设计时一定要注意性能
5>.与还可以利用类似的方法对jvm的性能做一个统计及优化等工作
3)xxxagent.dll开发的关键外在于取得相关的函数定义及签名等信息
取得函数名方法如下:
1>. 可以通过ClassLoader.java ClassLoader.c取得相关的方法
2>. 可以使用dumpbin.exe取得方法在java.dll中的导出名
dumpbin.exe /EXPORTS java.dll
3>. 通过javap -s -private ClassLoader取得相关类的签名信息
4>. 由于java语言及其实现是开源的所以可以取得相关的源代码 这样更易开发
伪代码如下:
1.注册虚拟机初始化结束事件 JVM_EVENT_VM_INIT
JNIEXPORT jint JNICALL Agent_OnLoad( JavaVM *jvm, char *options, void *reserved )
{
printf( "starting agent ......\n" );
error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_VM_INIT, (jthread)NULL);
return JNI_OK;
}
typedef jclass(JNICALL *lpDefineClass1_32)
(
JNIEnv *env,
jobject loader,
jstring name,
jbyteArray b,
jint off,
jint len,
jobject pd,
jstring source,
jboolean bVerify
);
//用于指向java.dll中的 _Java_java_lang_ClassLoader_defineClass1@36
//32位的函数指针
//64位的和这个不同
//可通过ClassLoader.java ClassLoader.c 及使用dumpbin.exe /EXPORTS java.dll查看即可得知参数及函数名
2. 响应虚拟机初始化结束事件 并注册要hook的函数 及取得要调用的函数指针
static void JNICALL jvmInitFinish( jvmtiEnv *jvmti_env, JNIEnv* env, jthread thr )
{
printf( "jvm Init finish ......\n" );
int iLen = env->RegisterNatives( jcls, jni_methes1_6, iLen );
HMOUDLE hJavaDll = LoadLibrary( "java.dll" );
funcDefineClass1_32 = (lpDefineClass1_32)(GetProcAddress(hJavaDll, "_Java_java_lang_ClassLoader_defineClass1@36" ));
funcDefineClass2_32 = (lpDefineClass2_32)(GetProcAddress(hJavaDll, "_Java_java_lang_ClassLoader_defineClass2@36" ));
}
注意其中的函数指针要定义正确 可以ClassLoader中得到正常的定义
3. 要hook的函数
JNIEXPORT jclass JNICALL
comdev_defineClass1_6(
JNIEnv *env,
jobject loader,
jstring name,
jbyteArray data,
jint offset,
jint length,
jobject pd,
jstring source,
jboolean verify
)
{
if( !strncmp( (const char*)LABEL, (const char*)originByte, strlen((const char*)LABEL)) )
{ //如果是已加密的类则执行解密操作
lPlainText = DES_DecryptByteArray( pCipher, pCharKey, LABEL, pPlainText, iCipherLen );
}
if( pPlainText )
{
cls = funcDefineClass1_32( env, loader, name, pByteArr, offset, lPlainText, pd, source, verify );
}
else
{ //如果是非加密类则直接执行java.dll中的原函数
cls = funcDefineClass1_32( env, loader, name, data, offset, length, pd, source, verify );
}
return cls;
}
fzq整理所得 欢迎转载
2.注意事项
由于java解密代码调用非常的频繁 所以一定要注意申请的资源要及时释放 否则会导致内存泄漏以至程序崩溃
目前该程序只适用于java 1.6 java1.5实现未作测试 java1.7不能使用 由于底层实现有变java1.7的兼容性开发还未做
3.程序解释
1)加密程序
可采用对称加密如需要可以再修改成其它加密算法 并对加密的文件做上加密标记
2)
1>.Java agent程序 java虚拟机在运行是使用-agentpath或-agentlib参数加载该dll 如
java -agentpath:C:\temp\xxxagent.dll=C:\temp\key.dat HelloWorld
其中的HelloWorld.class为已经加密过的类 当然如果这个类未加密也可以正常运行 xxxagent.dll会识别哪些类加密了
2>.该dll向jvm注册了一些本地的方法最重要的一个是defineClass1
3>.注册之后在做了解密处理之后还要调用jvm本身的函数defineClass1(这个函数位于java.dll中)
4>.此程序设计时一定要注意性能
5>.与还可以利用类似的方法对jvm的性能做一个统计及优化等工作
3)xxxagent.dll开发的关键外在于取得相关的函数定义及签名等信息
取得函数名方法如下:
1>. 可以通过ClassLoader.java ClassLoader.c取得相关的方法
2>. 可以使用dumpbin.exe取得方法在java.dll中的导出名
dumpbin.exe /EXPORTS java.dll
3>. 通过javap -s -private ClassLoader取得相关类的签名信息
4>. 由于java语言及其实现是开源的所以可以取得相关的源代码 这样更易开发
伪代码如下:
1.注册虚拟机初始化结束事件 JVM_EVENT_VM_INIT
JNIEXPORT jint JNICALL Agent_OnLoad( JavaVM *jvm, char *options, void *reserved )
{
printf( "starting agent ......\n" );
error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_VM_INIT, (jthread)NULL);
return JNI_OK;
}
typedef jclass(JNICALL *lpDefineClass1_32)
(
JNIEnv *env,
jobject loader,
jstring name,
jbyteArray b,
jint off,
jint len,
jobject pd,
jstring source,
jboolean bVerify
);
//用于指向java.dll中的 _Java_java_lang_ClassLoader_defineClass1@36
//32位的函数指针
//64位的和这个不同
//可通过ClassLoader.java ClassLoader.c 及使用dumpbin.exe /EXPORTS java.dll查看即可得知参数及函数名
2. 响应虚拟机初始化结束事件 并注册要hook的函数 及取得要调用的函数指针
static void JNICALL jvmInitFinish( jvmtiEnv *jvmti_env, JNIEnv* env, jthread thr )
{
printf( "jvm Init finish ......\n" );
int iLen = env->RegisterNatives( jcls, jni_methes1_6, iLen );
HMOUDLE hJavaDll = LoadLibrary( "java.dll" );
funcDefineClass1_32 = (lpDefineClass1_32)(GetProcAddress(hJavaDll, "_Java_java_lang_ClassLoader_defineClass1@36" ));
funcDefineClass2_32 = (lpDefineClass2_32)(GetProcAddress(hJavaDll, "_Java_java_lang_ClassLoader_defineClass2@36" ));
}
注意其中的函数指针要定义正确 可以ClassLoader中得到正常的定义
3. 要hook的函数
JNIEXPORT jclass JNICALL
comdev_defineClass1_6(
JNIEnv *env,
jobject loader,
jstring name,
jbyteArray data,
jint offset,
jint length,
jobject pd,
jstring source,
jboolean verify
)
{
if( !strncmp( (const char*)LABEL, (const char*)originByte, strlen((const char*)LABEL)) )
{ //如果是已加密的类则执行解密操作
lPlainText = DES_DecryptByteArray( pCipher, pCharKey, LABEL, pPlainText, iCipherLen );
}
if( pPlainText )
{
cls = funcDefineClass1_32( env, loader, name, pByteArr, offset, lPlainText, pd, source, verify );
}
else
{ //如果是非加密类则直接执行java.dll中的原函数
cls = funcDefineClass1_32( env, loader, name, data, offset, length, pd, source, verify );
}
return cls;
}
fzq整理所得 欢迎转载