带搬迁功能的 PE 装载机

问题描述 投票:0回答:1

我正在尝试学习PE格式以及PE加载器的工作原理,我以这个存储库为例,https://github.com/TheD1rkMtr/FilelessPELoader

FilelessPELoader不支持重定位。现在,如果 PE 加载器可以在 PE 的 ImageBase 上分配可执行内存,则可以加载并执行 PE 文件,否则将会失败。

    preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;
...
    pImageBase = (BYTE*)VirtualAlloc(
        preferAddr,
        ntHeader->OptionalHeader.SizeOfImage,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE);

所以我尽了最大努力自己实现这个功能,但不幸的是我没有得到想要的结果。这是我对

PELoader()
函数的更改,

void pe_loader(char* data, DWORD datasize) {

    masquerade_cmdline();

    DWORD chksum = 0;
    for (DWORD i = 0; i < datasize; i++) {
        chksum = data[i] * i + chksum / 3;
    };

    BYTE* pImageBase = NULL;
    LPVOID preferAddr = 0;
    DWORD OldProtect = 0;

    IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)get_nt_header(data);
    if (!ntHeader) {
        exit(0);
    }

    IMAGE_DATA_DIRECTORY* relocDir = get_pe_directory(data, IMAGE_DIRECTORY_ENTRY_BASERELOC);
    preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;

    HMODULE dll = LoadLibraryA("ntdll.dll");
    NtUnmapViewOfSectionProc NtUnmapViewOfSection = (NtUnmapViewOfSectionProc)
        GetProcAddress(dll, "NtUnmapViewOfSection");
    NtUnmapViewOfSection((HANDLE)-1, (LPVOID)ntHeader->OptionalHeader.ImageBase);

    //pImageBase = (BYTE*)VirtualAlloc(
    //    preferAddr,
    //    ntHeader->OptionalHeader.SizeOfImage,
    //    MEM_COMMIT | MEM_RESERVE,
    //    PAGE_EXECUTE_READWRITE);
    //if (!pImageBase) {
    //    if (!relocDir) {
    //        exit(0);
    //    }
    //    else {
    //        pImageBase = (BYTE*)VirtualAlloc(
    //            NULL,
    //            ntHeader->OptionalHeader.SizeOfImage,
    //            MEM_COMMIT | MEM_RESERVE,
    //            PAGE_EXECUTE_READWRITE);
    //        if (!pImageBase)
    //        {
    //            exit(0);
    //        }
    //    }
    //}

    pImageBase = (BYTE*)VirtualAlloc(NULL,
        ntHeader->OptionalHeader.SizeOfImage,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE);
    if (!pImageBase)
    {
        exit(0);
    }

    // Copy the PE file in allocated memory
    ntHeader->OptionalHeader.ImageBase = (size_t)pImageBase;
    memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);

    LPVOID lpRelocHdr = NULL;
    IMAGE_SECTION_HEADER* SectionHeaderArr =
        (IMAGE_SECTION_HEADER*)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS));

    for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
    {
        if (strcmp((char*)SectionHeaderArr[i].Name, ".reloc") == 0)
        {
            //lpImageRelocSection = (PIMAGE_SECTION_HEADER)SectionHeaderArr[i].VirtualAddress;
            lpRelocHdr = LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress);
        }

        memcpy(LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress),
            LPVOID(size_t(data) + SectionHeaderArr[i].PointerToRawData),
            SectionHeaderArr[i].SizeOfRawData);
    }


    printf("Executable memory : %p\n", pImageBase);
    printf("RW memory : %p\n", data);

    DWORD DeltaImageBase = (DWORD)pImageBase - ntHeader->OptionalHeader.ImageBase;
    IMAGE_BASE_RELOCATION* ImageBaseReloc = (IMAGE_BASE_RELOCATION*)lpRelocHdr;
    cout << hex << ImageBaseReloc << endl;
    
    while (ImageBaseReloc->VirtualAddress != 0)
    {
        DWORD RelocEntries = (ImageBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
        
        cout << "Virtual Address : " << hex << ImageBaseReloc->VirtualAddress << endl;
        cout << "Relocation Entries : " << hex << RelocEntries << endl;
        cout << "Size of Relocation Header : " << hex << (ImageBaseReloc->SizeOfBlock) << endl;
        cout << hex << ImageBaseReloc << endl;

        WORD* ImageRelocEntry = (WORD*)((char*)ImageBaseReloc + sizeof(IMAGE_BASE_RELOCATION));
        for (int i = 0; i < RelocEntries; i++)
        {
            //type is the first 4 bits of the relocation word
            int type = ImageRelocEntry[i] >> 12;
            // offset is the last 12 bits
            int offset = ImageRelocEntry[i] & 0x0fff;
            cout << type << "\t" << offset << endl;
            if (type == 0)
                continue;

            DWORD* ChangeAddr = (DWORD*)(pImageBase + ImageBaseReloc->VirtualAddress + offset);
            *ChangeAddr = DeltaImageBase;
            //memcpy(ChangeAddr, &DeltaImageBase, sizeof(DWORD));
        }

        ImageBaseReloc = (IMAGE_BASE_RELOCATION*)(((char*)ImageBaseReloc + ImageBaseReloc->SizeOfBlock));
    }
    

    // Fix the PE Import addr table
    repair_iat(pImageBase);

    // AddressOfEntryPoint
    size_t ret_addr = (size_t)(pImageBase)+ntHeader->OptionalHeader.AddressOfEntryPoint;

    // Jumping to the EntryPoint of the loaded PE
    std::cout << "  - Jumping..." << std::endl;

    //
    EnumThreadWindows(0, (WNDENUMPROC)ret_addr, 0);

    return;
}

我修改前的原函数代码可以在这里

找到

非常感谢您帮助告诉我我做错了什么以及如何修复代码?

另外,在解析重定位表和条目时,我还需要修复导入地址表,对吗?这是两个不同的事情?我也不太确定那部分。

谢谢

windows visual-c++ portable-executable
1个回答
0
投票

我又回到这个问题了…… 在网上寻找一些线索时,我发现了以下 YouTube 视频在此处输入链接描述。它帮助我了解我应该做什么以及我做错了什么。

所以我最终得到的工作代码是,

void pe_loader(char* data, DWORD datasize) {

    masquerade_cmdline();

    DWORD chksum = 0;
    for (DWORD i = 0; i < datasize; i++) {
        chksum = data[i] * i + chksum / 3;
    };

    BYTE* pImageBase = NULL;
    LPVOID preferAddr = 0;
    DWORD OldProtect = 0;

    IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)get_nt_header(data);
    if (!ntHeader) {
        exit(0);
    }

    IMAGE_DATA_DIRECTORY* relocDir = get_pe_directory(data, IMAGE_DIRECTORY_ENTRY_BASERELOC);
    preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;


    pImageBase = (BYTE*)VirtualAlloc(NULL,
        ntHeader->OptionalHeader.SizeOfImage,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE);
    if (!pImageBase)
    {
        exit(0);
    }

    // Copy the PE file in allocated memory
    memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);

    LPVOID lpRelocHdr = NULL;
    IMAGE_SECTION_HEADER* SectionHeaderArr =
        (IMAGE_SECTION_HEADER*)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS));

    for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
    {
        if (strcmp((char*)SectionHeaderArr[i].Name, ".reloc") == 0)
        {
            //lpImageRelocSection = (PIMAGE_SECTION_HEADER)SectionHeaderArr[i].VirtualAddress;
            lpRelocHdr = LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress);
        }

        memcpy(LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress),
            LPVOID(size_t(data) + SectionHeaderArr[i].PointerToRawData),
            SectionHeaderArr[i].SizeOfRawData);
    }


    printf("Executable memory : %p\n", pImageBase);
    printf("RW memory : %p\n", data);

    DWORD DeltaImageBase = (DWORD)pImageBase - ntHeader->OptionalHeader.ImageBase;
    IMAGE_BASE_RELOCATION* ImageBaseReloc = (IMAGE_BASE_RELOCATION*)lpRelocHdr;
    cout << hex << ImageBaseReloc << endl;
    
    while (ImageBaseReloc->VirtualAddress != 0)
    {
        DWORD RelocEntries = (ImageBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
        
        cout << "Virtual Address : " << hex << ImageBaseReloc->VirtualAddress << endl;
        cout << "Relocation Entries : " << hex << RelocEntries << endl;
        cout << "Size of Relocation Header : " << hex << (ImageBaseReloc->SizeOfBlock) << endl;
        cout << hex << ImageBaseReloc << endl;

        WORD* ImageRelocEntry = (WORD*)((char*)ImageBaseReloc + sizeof(IMAGE_BASE_RELOCATION));
        for (int i = 0; i < RelocEntries; i++)
        {
            //type is the first 4 bits of the relocation word
            int type = ImageRelocEntry[i] >> 12;
            // offset is the last 12 bits
            int offset = ImageRelocEntry[i] & 0x0fff;
            cout << type << "\t" << offset << endl;
            if (type == 0)
                continue;

            DWORD* ChangeAddr = (DWORD*)(pImageBase + ImageBaseReloc->VirtualAddress + offset);
            *ChangeAddr += DeltaImageBase;
        }

        ImageBaseReloc = (IMAGE_BASE_RELOCATION*)(((char*)ImageBaseReloc + ImageBaseReloc->SizeOfBlock));
    }
    

    // Fix the PE Import addr table
    repair_iat(pImageBase);

    // AddressOfEntryPoint
    size_t ret_addr = (size_t)(pImageBase)+ntHeader->OptionalHeader.AddressOfEntryPoint;

    // Jumping to the EntryPoint of the loaded PE
    std::cout << "  - Jumping..." << std::endl;

    //
    EnumThreadWindows(0, (WNDENUMPROC)ret_addr, 0);

    return;
}

我最初的代码有不止一个问题,指针算术、设置

    ntHeader->OptionalHeader.ImageBase = (size_t)pImageBase;

最后,重定位表上的每个条目都指向基于基地址的地址,我们需要将增量添加到当前值。

感谢该视频的创作者。

© www.soinside.com 2019 - 2024. All rights reserved.