手动加载DLL(PE文件)

以前学重载内核时学到了手动加载一个PE文件,想在Ring3层也实现一遍,不过在GitHub上看到有现成的源码了就不自己写了。本篇文章就分析一下这个mmLoader,看看怎么实现手动加载PE文件

阅读前需要了解PE文件结构,还不了解的自行恶补

这个库为了实现注入ShellCode,自己实现了memset等常用库函数,还自己定义了要用到的库函数表。实际使用时,如果不用注入ShellCode使用常规的库函数就好

虽然这个库是用来加载DLL的,修改一下也可以用来加载EXE

加载PE文件的主要步骤:

  1. 将PE头和各个节映射到内存
  2. 重定位
  3. 修复IAT
  4. 调用模块入口点

映射文件到内存

这一步就是把PE文件的内容读到内存相应的位置,要注意文件中各节的对齐和内存中的对齐是不一样的

/// <summary>
/// Maps all the sections.
/// </summary>
/// <param name="pMemModule">The <see cref="MemModule" /> instance.</param>
/// <returns>True if successful.</returns>
BOOL MapMemModuleSections(PMEM_MODULE pMemModule, LPVOID lpPeModuleBuffer)
{
    // Validate
    if (NULL == pMemModule || NULL == pMemModule->pNtFuncptrsTable || NULL == lpPeModuleBuffer)
        return FALSE;

    // Function pointer
    // VirtualAlloc的函数指针,ShellCode用的,实际使用时直接写VirtualAlloc即可,后面类似的不再赘述
    Type_VirtualAlloc pfnVirtualAlloc = (Type_VirtualAlloc)(pMemModule->pNtFuncptrsTable->pfnVirtualAlloc);
    Type_VirtualFree pfnVirtualFree = (Type_VirtualFree)(pMemModule->pNtFuncptrsTable->pfnVirtualFree);

    // Convert to IMAGE_DOS_HEADER
    // PE文件头部指针,即DOS头
    PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)(lpPeModuleBuffer);

    // Get the pointer to IMAGE_NT_HEADERS
    // NT头,MakePointer是个宏,返回某指针+某偏移量后的新指针
    PIMAGE_NT_HEADERS pImageNtHeader = MakePointer(
        PIMAGE_NT_HEADERS, pImageDosHeader, pImageDosHeader->e_lfanew);

    // Get the section count
    int nNumberOfSections = pImageNtHeader->FileHeader.NumberOfSections;

    // Get the section header
    // 节头
    PIMAGE_SECTION_HEADER pImageSectionHeader = MakePointer(
        PIMAGE_SECTION_HEADER, pImageNtHeader, sizeof(IMAGE_NT_HEADERS));

    // Find the last section limit
    // 计算整个模块尺寸
    DWORD dwImageSizeLimit = 0;
    for (int i = 0; i < nNumberOfSections; ++i)
    {
        if (0 != pImageSectionHeader[i].VirtualAddress)
        {
            if (dwImageSizeLimit < (pImageSectionHeader[i].VirtualAddress + pImageSectionHeader[i].SizeOfRawData))
                dwImageSizeLimit = pImageSectionHeader[i].VirtualAddress + pImageSectionHeader[i].SizeOfRawData;
        }
    }

    // Align the last image size limit to the page size
    // 按页对齐
    dwImageSizeLimit = (dwImageSizeLimit + pMemModule->dwPageSize - 1) & ~(pMemModule->dwPageSize - 1);

    // Reserve virtual memory 
    // 优先使用ImageBase作为模块基址,分配一块内存
    LPVOID lpBase = pfnVirtualAlloc(
        (LPVOID)(pImageNtHeader->OptionalHeader.ImageBase), 
        dwImageSizeLimit,
        MEM_RESERVE | MEM_COMMIT, 
        PAGE_READWRITE);

    // Failed to reserve space at ImageBase, then it's up to the system
    // 这个基址不能使用,让系统随机选另一个基址(后面需要重定位)
    if (NULL == lpBase)
    {
        // Reserver memory in arbitrary address
        lpBase = pfnVirtualAlloc(
            NULL, 
            dwImageSizeLimit,
            MEM_RESERVE | MEM_COMMIT, 
            PAGE_READWRITE);

        // Failed again, return 
        if (NULL == lpBase)
        {
            pMemModule->dwErrorCode = MMEC_ALLOCATED_MEMORY_FAILED;
            return FALSE;
        }
    }

    // Commit memory for PE header
    LPVOID pDest = pfnVirtualAlloc(lpBase, pImageNtHeader->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE);
    if (!pDest)
    {
        pMemModule->dwErrorCode = MMEC_ALLOCATED_MEMORY_FAILED;
        return FALSE;
    }

    // Copy the data of PE header to the memory allocated
    // 复制PE头
    mml_memmove(pDest, lpPeModuleBuffer, pImageNtHeader->OptionalHeader.SizeOfHeaders);

    // Store the base address of this module.
    pMemModule->lpBase = pDest;
    pMemModule->dwSizeOfImage = pImageNtHeader->OptionalHeader.SizeOfImage;
    pMemModule->bLoadOk = TRUE;

    // Get the DOS header, NT header and Section header from the new PE header buffer
    pImageDosHeader = (PIMAGE_DOS_HEADER)pDest;
    pImageNtHeader = MakePointer(PIMAGE_NT_HEADERS, pImageDosHeader, pImageDosHeader->e_lfanew);
    pImageSectionHeader = MakePointer(PIMAGE_SECTION_HEADER, pImageNtHeader, sizeof(IMAGE_NT_HEADERS));

    // Map all section data into the memory
    // 复制所有的节
  
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值