Часто интересовался структурой 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 → Глава третья. Как Братец Кролик уменьшал размер программы