• XSS.stack #1 – первый литературный журнал от юзеров форума

При переборе Resource Directory, приходят не верные данные

ymmfty0

HDD-drive
Пользователь
Регистрация
28.10.2022
Сообщения
29
Реакции
5
Привет всем! Я изучаю формат PE файлов и хотел написать функцию, которая будет рекурсивно перебирать подкаталоги в Resource Directory
Если посмотреть в CFF Explorer, то можно увидеть то что элементов 4

1734512867054.png


Для этого я написал вот такую функцию

C++:
void EnumerateResources(const PBYTE resourceSectionAddr , const PIMAGE_RESOURCE_DIRECTORY entry , int depth = 0)
{
    if (resourceSectionAddr == nullptr || entry == nullptr)
    {
        std::cerr << "Invalid pointer to resource section or directory!" << std::endl;
        return;
    }

    auto directory_entries = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(
        reinterpret_cast<const PBYTE>(entry) + sizeof(IMAGE_RESOURCE_DIRECTORY)
        );
    
    if (directory_entries == nullptr)
    {
        std::cerr << "Invalid pointer to directory entries!" << std::endl;
        return;
    }

    size_t entries_size = (entry->NumberOfIdEntries + entry->NumberOfNamedEntries) - 1;
    for (size_t i = 0; i <= entries_size  ; ++i)
    {
        if (directory_entries[i].DataIsDirectory)
        {
            std::cout << std::string(depth * 2, ' ') << "Directory ";

            if (directory_entries[i].NameIsString)
            {
                auto dir_string = reinterpret_cast<PIMAGE_RESOURCE_DIR_STRING_U>(resourceSectionAddr + directory_entries[i].NameOffset);

                std::wstring name(dir_string->NameString, dir_string->Length);
                std::wcout << L"Name: " << name << std::endl;
            }
            else
                std::cout << std::hex << "Id: " << directory_entries[i].Id << std::endl;
            

            auto data_entry_resource_directory = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(
                reinterpret_cast<PBYTE>(entry) + directory_entries[i].OffsetToDirectory
                );
            EnumerateResources(resourceSectionAddr, data_entry_resource_directory, depth + 1);
        }
        else
        {
            std::cout << std::string(depth * 2, ' ') << "Resource ";
            if ( directory_entries[i].NameOffset & IMAGE_RESOURCE_NAME_IS_STRING )
            {
                auto dir_string = reinterpret_cast<PIMAGE_RESOURCE_DIR_STRING_U>(
                    reinterpret_cast<PBYTE>(resourceSectionAddr) + directory_entries[i].NameOffset
                    );
                std::wstring name(dir_string->NameString, dir_string->Length);
                std::wcout << L"name: " << name << std::endl;
            }
            else
            {
                std::cout << "id: " << std::hex << directory_entries[i].Id << std::endl;
            }
        }
    }
    
    return;
}

Код идет корректно до тех пор , пока не сталкивается с директорией IDI_CALC_ICON начинают приходить неверные данные

1734512995238.png


Не могу понять в чем может быть причина, почему данные не приходят корректно

Вот код в функции main

Код:
DWORD RvaToOffset(DWORD rva, DWORD VirtualOffset, DWORD RawOffset)
{
    return rva - VirtualOffset + RawOffset;
}

int main()
{

    std::ifstream input_file("C:\\Windows\\System32\\calc.exe", std::ios::binary);

    input_file.seekg(0, std::ios_base::end);

    auto length = input_file.tellg();
    input_file.seekg(0, std::ios_base::beg);

    std::vector<BYTE> pe_file_buffer(length);
    input_file.read(reinterpret_cast<char*>(pe_file_buffer.data()), length);

    auto pe_dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(pe_file_buffer.data());
    if (pe_dos_header->e_magic != IMAGE_DOS_SIGNATURE)
    {
        std::cerr << "[!] Incorrect DOS Header signature" << std::endl;
        return -1;
    }

    auto pe_nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(pe_file_buffer.data() + pe_dos_header->e_lfanew);
    if (pe_nt_headers->Signature != IMAGE_NT_SIGNATURE)
    {
        std::cerr << "[!] Incorrect NT Headers Signature " << std::endl;
        return -2;
    }

    auto pe_data_directory = static_cast<IMAGE_DATA_DIRECTORY>(pe_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);

    auto section_headers = reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<PBYTE>(pe_nt_headers) + sizeof(IMAGE_NT_HEADERS));

    std::string resource_section_name = ".rsrc";

    for (size_t i = 0; i < pe_nt_headers->FileHeader.NumberOfSections; ++i)
    {
        if (std::memcmp(section_headers->Name, resource_section_name.c_str(), resource_section_name.size()) == 0)
            break;
        ++section_headers;
    }

    DWORD rva_to_offset = RvaToOffset(pe_data_directory.VirtualAddress, section_headers->VirtualAddress, section_headers->PointerToRawData);

    auto resource_table = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(pe_file_buffer.data() + rva_to_offset);

    EnumerateResources( reinterpret_cast<PBYTE>(resource_table) , resource_table);

    return 1;
}

Не могу разобраться почему приходят эти не корректные данные.
 
Решение
1. Есть проблемы с адресацией, OffsetToDirectory хранит RVA от начала секции .rsrc, а не смещение относительно текущей директории, поэтому нужно добавить resourceSectionAddr в калькуляцию:

Код:
auto data_entry_resource_directory = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(

    resourceSectionAddr + directory_entries[i].OffsetToDirectory

);

2. entries_size
надо так

Код:
size_t entries_size = entry->NumberOfIdEntries + entry->NumberOfNamedEntries;

3. Посмотри врено ли считаются адреса секции rsrc
1. Есть проблемы с адресацией, OffsetToDirectory хранит RVA от начала секции .rsrc, а не смещение относительно текущей директории, поэтому нужно добавить resourceSectionAddr в калькуляцию:

Код:
auto data_entry_resource_directory = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(

    resourceSectionAddr + directory_entries[i].OffsetToDirectory

);

2. entries_size
надо так

Код:
size_t entries_size = entry->NumberOfIdEntries + entry->NumberOfNamedEntries;

3. Посмотри врено ли считаются адреса секции rsrc
 
Решение
size_t entries_size = entry->NumberOfIdEntries + entry->NumberOfNamedEntries;
Это не так, тут нужно отнимать, потому-что это количество, а массив начинается с 0 , так что нужно отнимать - 1

1. Есть проблемы с адресацией, OffsetToDirectory хранит RVA от начала секции .rsrc, а не смещение относительно текущей директории, поэтому нужно добавить resourceSectionAddr в калькуляцию:
да , это сработало. Спасибо, проблема была в том , нужно брать адрес корневой директории и прибавлять OffsetToDirectory.
 
Это не так, тут нужно отнимать, потому-что это количество, а массив начинается с 0 , так что нужно отнимать - 1
Нет.
NumberOfIdEntries и NumberOfNamedEntries дают общее количество записей, а не индексы. Чтобы пройти массив записей, индексы начинаются с 0 и заканчиваются на entries_size - 1. Но цикл:
for (size_t i = 0; i < entries_size; ++i) уже корректно это обрабатывает, поскольку условие i < entries_size останавливает итерацию перед выходом за пределы массива. Твой код приводит к недостаточной итерации. Последняя запись (с индексом entries_size - 1) будет пропущена, потому что ты заранее уменьшил общее количество записей.
 
Нет.
NumberOfIdEntries и NumberOfNamedEntries дают общее количество записей, а не индексы. Чтобы пройти массив записей, индексы начинаются с 0 и заканчиваются на entries_size - 1. Но цикл:
for (size_t i = 0; i < entries_size; ++i) уже корректно это обрабатывает, поскольку условие i < entries_size останавливает итерацию перед выходом за пределы массива. Твой код приводит к недостаточной итерации. Последняя запись (с индексом entries_size - 1) будет пропущена, потому что ты заранее уменьшил общее количество записей.
У меня в коде не так for (size_t i = 0; i < entries_size; ++i), а так for (size_t i = 0; i <= entries_size ; ++i), так что количество итераций , будет столько сколько надо. В целом разницы нет, что я делаю - 1 , что оставлю так, проблема была в другом
 
Нет.
NumberOfIdEntries и NumberOfNamedEntries дают общее количество записей, а не индексы. Чтобы пройти массив записей, индексы начинаются с 0 и заканчиваются на entries_size - 1. Но цикл:
for (size_t i = 0; i < entries_size; ++i) уже корректно это обрабатывает, поскольку условие i < entries_size останавливает итерацию перед выходом за пределы массива. Твой код приводит к недостаточной итерации. Последняя запись (с индексом entries_size - 1) будет пропущена, потому что ты заранее уменьшил общее количество записей.
Если брать в твоем случае , количество итераций будет точно таким же , в твоем случае От 0 до entries_size - 1 , а в моем От 0 до entries_size , потомучто в моем случае цикл for (size_t i = 0; i <= entries_size ; ++i),
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх