Часто интересовался структурой PE файлов,у меня вызывает вопрос такая вещь как RVA адрес,если быть точнее то меня интересует как он вычисляется, к примеру RVA секции .text
RVA - Relative Virtual Address это смещение секции файла относительно базового адреса образа в виртуальной памяти. К примеру, библиотека .dll загружена по адресу 0x400000, RVA секции .text 0x1000, виртуальный адрес .text - 0x401000. Зависит от того, откуда вам нужно вычислить RVA. Если из заголовка PE - смотрите IMAGE_SECTION_HEADER.
Раздел Section Headers: https://docs.microsoft.com/en-gb/windows/win32/debug/pe-format, поле VirtualAddress (сбивающее с толку название) содержит RVA секции. Пример: RVA+IMAGE_BASE=VA=0x00001000+0x00400000=0x00401000
насколько верно понял то RVA адрес связан с SectionAlignment,просто я хотел понять как вычисляется RVA адрес прежде чем будет записан в IMAGE_SECTION_HEADER.VirtualAddress
Конкретно RVA секции вычисляется примерно так: размер загруженного в память заголовка + размер загруженной в память секции 1 (например, .text) + размер загруженной в память секции 2 + ... +размер загруженной в память секции n, где 1..n это секции расположенные перед нашей секцией (все это естественно выравнено на сколько там позволяет SectionAlignment, загрузчик и размер страницы). Можно ли от этого правила отступить и написать RVA от балды вопрос интересный, какие проверки будет делать загрузчик, надо тестировать.
А вот ещё немного мягких французских коденгов Ф-ия CmcPeMapRva "проецирует" RVA, указывающий внутрь PE- образа на смещение внутри файла на диске. ULONG всегда 32-бит, т.к. в 64-битной винде размер PE-образа 32-битный, как и на 32-битной винде. А структурка CMC_PE_DESC нам нужна чтобы задать, хотим ли мы работать с PE-образом, загруженым в память (когда секции отображены по их Virtual Addresses) или передаем PE- образ в том виде, в котором он считан с диска ("raw"). Ну и указатель на сам образ тоже в этой структурке. А TODO: overlay - можешь написать сам) Код (C): void CmcPeGetHeaders(const void* ImageBase, void** Nt32or64Headers, PIMAGE_SECTION_HEADER* SectionHeader) { PIMAGE_DOS_HEADER Dos = (PIMAGE_DOS_HEADER)ImageBase; // NT headers 32 and 64 differ only in Optional header PIMAGE_NT_HEADERS32 Nt32 = (PIMAGE_NT_HEADERS32)((ULONG_PTR)ImageBase + Dos->e_lfanew); if (Nt32or64Headers) { *Nt32or64Headers = (void*)Nt32; } if (SectionHeader) { *SectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)&Nt32->OptionalHeader + Nt32->FileHeader.SizeOfOptionalHeader); } } BOOLEAN CmcPeIs64Bit(const void* ImageBase) { PIMAGE_NT_HEADERS32 Nt; CmcPeGetHeaders(ImageBase, (void**)&Nt, NULL); return Nt->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ? TRUE : FALSE; } // |ImageBase| can be either mapped or raw, we don't care, we're just looking into its headers int CmcPeLocateRva(const void* ImageBase, ULONG Rva, PULONG DiskOfs, PULONG OfsInsideSec, PIMAGE_SECTION_HEADER* SecPtr, PUSHORT SecIdx) { ULONG SectionAlignment, SizeOfHeaders; union { PIMAGE_NT_HEADERS32 Nt32; PIMAGE_NT_HEADERS64 Nt64; } Bitness; PIMAGE_SECTION_HEADER SecHdr; CmcPeGetHeaders(ImageBase, (void**)&Bitness.Nt32, &SecHdr); if (CmcPeIs64Bit(ImageBase)) { SectionAlignment = Bitness.Nt64->OptionalHeader.SectionAlignment; SizeOfHeaders = Bitness.Nt64->OptionalHeader.SizeOfHeaders; } else { SectionAlignment = Bitness.Nt32->OptionalHeader.SectionAlignment; SizeOfHeaders = Bitness.Nt64->OptionalHeader.SizeOfHeaders; } ULONG RetDiskOfs, RetOfsInsideSec; PIMAGE_SECTION_HEADER RetSecPtr; USHORT RetSecIdx; if (Rva < SizeOfHeaders) { // Supplied RVA points to headers RetDiskOfs = Rva; RetOfsInsideSec = 0; RetSecPtr = NULL; RetSecIdx = 0; goto Success; } // // TODO: overlay // // FILE_HEADERs are same for 32- and 64- bit structs for (WORD i = 0; i < Bitness.Nt32->FileHeader.NumberOfSections; i++) { ULONG SecRawSize = ALIGN_UP_BY(SecHdr[i].SizeOfRawData, SectionAlignment); ULONG SecVa = SecHdr[i].VirtualAddress; // ULONG secEndRva = SecHdr[i].VirtualAddress + SecHdr[i].Misc.VirtualSize; if (Rva >= SecVa && Rva < SecVa + SecRawSize) { RetSecIdx = i; RetOfsInsideSec = Rva - SecVa; RetSecPtr = &SecHdr[RetSecIdx]; RetDiskOfs = SecHdr[RetSecIdx].PointerToRawData + RetOfsInsideSec; goto Success; } } // No sections found return -1; Success: if (DiskOfs != NULL) { *DiskOfs = RetDiskOfs; } if (OfsInsideSec != NULL) { *OfsInsideSec = RetOfsInsideSec; } if (SecPtr != NULL) { *SecPtr = RetSecPtr; } if (SecIdx != NULL) { *SecIdx = RetSecIdx; } return 0; } PVOID CmcPeMapRva(const CMC_PE_DESC* PeDesc, ULONG Rva) { if (!PeDesc->IsImageMapped) { ULONG DiskOfs; if (CmcPeLocateRva(PeDesc->Image, Rva, &DiskOfs, NULL, NULL, NULL) < 0) { return NULL; } return (PVOID)((ULONG_PTR)PeDesc->Image + DiskOfs); } else { return (PVOID)((ULONG_PTR)PeDesc->Image + Rva); } } Код (C): typedef struct _CMC_PE_DESC { void* Image; BOOLEAN IsImageMapped; } CMC_PE_DESC;
Entropy, диапазон поля SectionAlignment варьируется от 16-байт до 64К, управляется ключами компилятора, по умолчанию значение SectionAlignment = 0x200 = 512 Сказки дядюшки Римуса о x64 → Глава третья. Как Братец Кролик уменьшал размер программы
скорее всего rva адрес зависит значения поля SectionAlignment,во всех pe-файлах rva начинается с 0x1000
rva это смещение в отображенном в память файле, отличается от сырых(файловых) из за страничной гранулярности памяти(0x1000 это размер физической страницы)
видимо такая страничная гранулярность во всех версиях Windows,у меня есть мысль,если значения rva адреса на кратно SectionAlignment тогда неправилен,то есть rva адрес может быть любым главное что бы он был кратен SectionAlignment
На меньшей гранулярности чем страница не может быть изменен тип памяти, те атр. секции. Код (Text): if (((Hdr)->OptionalHeader).SectionAlignment < ((Hdr)->OptionalHeader).FileAlignment) { \ return STATUS_INVALID_IMAGE_FORMAT; \ } - ядерная проверка валидности. PE Format