Всем привет,
собственно исследую исполняемые файлы, и работаю с ними именно как с файлами.
Столкнулся с проблемой чтения тел секций файла (таблица импорта, экспорта, ресурсы, релоки и т.д.), потому как все они после заголовка DOS/NT/Таблицы секций, обозначены относительными смещениями уже в виртуальном пространстве, а мне нужно получить смещение в файле на диске.
Пытаюсь прочитать таблицу экспорта из dll вот так:
(Код Lazarus, описание типов можно найти в winnt.h по их имени справа)
Код:
TImageDOSHeader = IMAGE_DOS_HEADER;
TImageDataDirectory = IMAGE_DATA_DIRECTORY;
TImageExportDirectory = IMAGE_EXPORT_DIRECTORY;
TImageSectionHeader = IMAGE_SECTION_HEADER;
TImageFileHeader = IMAGE_FILE_HEADER;
TImageOptionalHeader32 = IMAGE_OPTIONAL_HEADER32;
TImageOptionalHeader64 = IMAGE_OPTIONAL_HEADER64;
TImageNTHeaders64 = IMAGE_NT_HEADERS64;
TImageNTHeaders32 = IMAGE_NT_HEADERS32;
function TWinLibReader.LoadFile(const sFile: string): boolean;
var
Stream: TMemoryStream;
DOSHeader: TImageDOSHeader;
PEHeader32: TImageNTHeaders32;
PEHeader64: TImageNTHeaders64;
Magic: word;
bMode64: boolean;
iSectionsCount: integer;
iNTOffset: longint;
Sections: TImageSectionHeaders;
Section, ExpertsSect: TImageSectionHeader;
iExportsOffset: DWORD;
PEExportsDir: TImageDataDirectory;
PEExports: TImageExportDirectory;
i, iReaded: integer;
iExpoortsSec: word;
function GetSectionWithinRVA(Sections: TImageSectionHeaders; iSectionCount: word; RVA: DWORD; var Section: word): DWORD;
var
i: integer;
Sec: TImageSectionHeader;
NewRVA: DWORD;
begin
Result := 0;
NewRVA := 0;
for i := 0 to iSectionCount - 1 do
begin
Sec := Sections[i];
if ((RVA >= Sec.VirtualAddress) and (RVA < (Sec.VirtualAddress + Sec.SizeOfRawData))) then
begin
NewRVA := RVA - Sec.VirtualAddress;
NewRVA := NewRVA + Sec.PointerToRawData;
Result := NewRVA;
Section := i;
Break;
end;
end;
end;
begin
Result := False;
bMode64 := False;
try
if not FileExists(sFile) then
begin
SetErrorStr('Input file "' + sFile + '" not found');
exit;
end;
Stream := TMemoryStream.Create();
try
Stream.LoadFromFile(sFile);
if (Stream.Size = 0) then
begin
SetErrorStr('Input file "' + sFile + '" have ZERO (0) size in bytes. Nothing to read.');
exit;
end;
FillChar(DOSHeader, SizeOf(TImageDOSHeader), #0);
FillChar(PEHeader32, SizeOf(TImageNTHeaders32), #0);
FillChar(PEHeader64, SizeOf(TImageNTHeaders64), #0);
FillChar(PEExportsDir, SizeOf(TImageDataDirectory), #0);
FillChar(PEExports, SizeOf(TImageExportDirectory), #0);
Stream.Seek(0, soFromBeginning);
Stream.Read(DOSHeader, SizeOf(TImageDOSHeader));
Magic := DOSHeader.e_magic;
if (Magic <> IMAGE_DOS_SIGNATURE) and (Magic <> IMAGE_NT_SIGNATURE) then
begin
SetErrorStr('Uknown signature "0x' + IntToHex(Magic, 4) + '" in header. File not supported.');
exit;
end;
//If we in a DOS header, need relocate to DOSHeader.e_lfanew & read some of TImageNTHeaders
iNTOffset := 0;
if (Magic = IMAGE_DOS_SIGNATURE) then
begin
iNTOffset := DOSHeader.e_lfanew;
Stream.Seek(iNTOffset, soFromBeginning);
iReaded := Stream.Read(PEHeader32, SizeOf(TImageNTHeaders32));
end;
if (Magic = IMAGE_NT_SIGNATURE) then
begin
Stream.Seek(iNTOffset, soFromBeginning);
iReaded := Stream.Read(PEHeader32, SizeOf(TImageNTHeaders32));
end;
Magic := PEHeader32.Signature;
if (Magic <> IMAGE_NT_SIGNATURE) then
begin
SetErrorStr('NT header has a wrong Signature "0x' + IntToHex(Magic, 4) + '", expected "0x' + IntToHex(IMAGE_NT_SIGNATURE, 4) + '. Abort."');
exit;
end;
Magic := PEHeader32.FileHeader.Machine;
if (Magic <> IMAGE_FILE_MACHINE_I386) and (Magic <> IMAGE_FILE_MACHINE_AMD64) and (Magic <> IMAGE_FILE_MACHINE_IA64) then
begin
SetErrorStr('File not supported via machine type "0x' + IntToHex(Magic, 4) + '", expected one of "' +
IntToHex(IMAGE_FILE_MACHINE_I386, 4) + '"(i386),"' + IntToHex(IMAGE_FILE_MACHINE_AMD64, 4) + '" (AMD64),"' +
IntToHex(IMAGE_FILE_MACHINE_IA64, 4) + '"(IA64)');
exit;
end;
//Need read TImageNTHeaders64
bMode64 := (Magic <> IMAGE_FILE_MACHINE_I386);
if bMode64 then
begin
Stream.Seek(iNTOffset, soFromBeginning);
FillChar(PEHeader64, SizeOf(TImageNTHeaders64), #0);
FillChar(PEHeader32, SizeOf(TImageNTHeaders32), #0); //garbage
iReaded := Stream.Read(PEHeader64, SizeOf(TImageNTHeaders64));
end;
if bMode64 then
iSectionsCount := PEHeader64.FileHeader.NumberOfSections
else
iSectionsCount := PEHeader32.FileHeader.NumberOfSections;
if (iSectionsCount < 1) then
begin
SetErrorStr('File do not have any sections.');
exit;
end;
SetLength(Sections, iSectionsCount);
for i := 0 to iSectionsCount - 1 do
begin
FillChar(Sections[i], SizeOf(TImageSectionHeader), #0);
Stream.Read(Sections[i], SizeOf(TImageSectionHeader));
end;
if bMode64 then
PEExportsDir := PEHeader64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
else
PEExportsDir := PEHeader32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (PEExportsDir.VirtualAddress = 0) then
begin
SetErrorStr('File has no export section(VirtualAddress = 0)');
exit;
end;
iExportsOffset := 0;
iExportsOffset := GetSectionWithinRVA(Sections, iSectionsCount, PEExportsDir.VirtualAddress, iExpoortsSec);
ExpertsSect := Sections[iExpoortsSec];
if (iExportsOffset = 0) then
begin
SetErrorStr('RVAToOffset return -1. Abort.');
exit;
end;
Stream.Seek(iExportsOffset, soBeginning);
//Stream.Read(PEExports, SizeOf(TImageExportDirectory));
//Exports it is first section in the file by default (can it me in other order?)
//Mb in packers/protectors, not sure
//so I'm interested in the normal libraries
//Stream.Read(PEExports, SizeOf(TImageSectionHeader));
Result := True;
finally
FreeAndNil(Stream);
end;
except
on E: Exception do
begin
SetErrorStr('An exception occured while loading file "' + sFile + '".' + #13#10 + 'Message: ' + E.Message);
end;
end;
end;
Собственно я нахожу секцию в которой находится VirtualAddress каталога экспорта (IMAGE_DIRECTORY_ENTRY_EXPORT), но это ни разу не смещение в файле, а смещение в памяти.
Как RVA/VA конвертировать в File Offset?
ImageHlp, PsAPI и прочие JvPeImage и PE Image library for DElphi/Lazarus - не предлагать, по той простой причине, что код должен работать в Linux, а все указанное привязано к винде так или иначе, в т.ч. копипаста паскалевской функции ListDLLFunctions из интернетов.