Приветствую! Есть функция: Код (Text): procedure ListDLLExports(FileName: String); type TDWordArray = array [0..$FFFFF] of DWORD; var Name: String; i, dirsize: Cardinal; imageinfo: LoadedImage; pNameRVAs: ^TDWordArray; pDummy: PImageSectionHeader; pExportDirectory: PImageExportDirectory; begin if MapAndLoad(PChar(FileName), nil, @imageinfo, True, True) then begin try pExportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress, False, IMAGE_DIRECTORY_ENTRY_EXPORT, dirsize); if (pExportDirectory <> nil) then begin pNameRVAs := ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress, DWORD(pExportDirectory^.AddressOfNames), pDummy); for i := 0 to pExportDirectory^.NumberOfNames - 1 do begin Name := PChar(ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress, pNameRVAs^[i], pDummy)); Show(Name); end; end; finally UnMapAndLoad(@imageinfo); end; end; end; Показывает список экспортируемых функций у длл. Хочу переписать ее в таком виде: function ListDLLExports(FileName, ProcName: String): Pointer; Чтобы результат ее выполнения был такой же как у GetProcAddress. Основная сложность: Не знаю откуда вытащить адресс функции. Предполагаю что из этой структуры: Код (Text): PImageExportDirectory = ^TImageExportDirectory; _IMAGE_EXPORT_DIRECTORY = packed record Characteristics: DWord; TimeDateStamp: DWord; MajorVersion: Word; MinorVersion: Word; Name: DWord; Base: DWord; NumberOfFunctions: DWord; NumberOfNames: DWord; AddressOfFunctions: ^PDWORD; AddressOfNames: ^PDWORD; AddressOfNameOrdinals: ^PWord; end; Подскажите пожалуйста, как это сделать
Твой исходник работает с файлом. Указатель на функцию появляется после загрузки файла DLL в память. Поэтому никакие манипуляции с файлом не помогут тебе получить то, что совсем в другом месте хранится. Если бы все так просто было, народ бы не обсуждал до посинения как скрытым образом вызвать GetProcAddress.
DeniskaV В том же цикле чтения имен читаешь значение ординала функции из массива AddressOfNameOrdinals, вычитаешь из него значение Base и по полученному индексу читаешь RVA из массива AddressOfFunctions. Однако полученный адрес м.б. ссылкой на имя функции из другой dll (forwarded экспорт), поэтому нужно еще проверить - если полученный адрес лежит за пределами таблицы экспорта, то это реальный адрес экспортируемой функции, если же внутри, то это указатель на составное имя - например, в kernel32 адрес ф-ии HeapAlloc даст ссылку не на саму ф-ю, а на строку "ntdll.RtlAllocateHeap" PS: Если ты грузишь dll не через LoadLibrary, а просто мапишь dll из файла, то имеет смысл получать только RVA функций
Спасибо! Нашел решение: Код (Text): function GetAddr(szDll, szFunc: PChar): Pointer; var pNtHeaders: PImageNtHeaders; ExportAddr: TImageDataDirectory; ImageBase: DWORD; IED: PImageExportDirectory; i: Integer; FuntionAddr: DWORD; NamesCursor: PDWORD; OrdinalCursor: PWORD; Ordinal: DWORD; begin Result := nil; ZeroMemory(@ExportAddr, SizeOf(ExportAddr)); ImageBase := GetModuleHandle(szDll); if PWORD(ImageBase)^ = IMAGE_DOS_SIGNATURE then begin pNtHeaders := Pointer(ImageBase + DWORD(PImageDosHeader(ImageBase)^._lfanew)); if (pNtHeaders^.Signature = IMAGE_NT_SIGNATURE) and (pNtHeaders^.FileHeader.Machine = IMAGE_FILE_MACHINE_I386) then begin ExportAddr := pNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; if ExportAddr.VirtualAddress <> 0 then Inc(ExportAddr.VirtualAddress, ImageBase) else ExportAddr.Size := 0; end; end; if (ImageBase = 0) or (ExportAddr.VirtualAddress = 0) then Exit; IED := PImageExportDirectory(ExportAddr.VirtualAddress); i := 1; NamesCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNames)); OrdinalCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNameOrdinals)); while i < Integer(IED^.NumberOfNames) do begin Ordinal := OrdinalCursor^ + IED^.Base; FuntionAddr := ImageBase + DWORD(IED^.AddressOfFunctions); FuntionAddr := ImageBase + PDWORD(FuntionAddr + (Ordinal - 1) * 4)^; if lstrcmpi(szFunc, PAnsiChar(ImageBase + PDWORD(NamesCursor)^)) = 0 then Result := Pointer(FuntionAddr); Inc(i); Inc(NamesCursor); Inc(OrdinalCursor); end; end;
DeniskaV А если функция первая/вторая в списке? ... Break забыл влепить! Да и вообще нужна проверка значения FuntionAddr на указание внутрь массива (выше leo говорил о forwarded экспорте).
Ммм.. Подскажите плз как это сделать, я в этом плохо разбираюсь Break Нужно там где: Result := Pointer(FuntionAddr); ???
Возможно кому то тоже пригодится, код с учетом: > например, в kernel32 адрес ф-ии HeapAlloc даст ссылку не > на саму ф-ю, а на строку "ntdll.RtlAllocateHeap" Код (Text): program Project1; {$APPTYPE CONSOLE} uses Windows, SysUtils; var FuncName: AnsiString; pNtHeaders: PImageNtHeaders; ExportAddr: TImageDataDirectory = (VirtualAddress: 0; Size: 0); ImageBase: DWORD; IED: PImageExportDirectory; IID: DWORD; I: Integer; FuntionAddr: DWORD; NamesCursor: PDWORD; OrdinalCursor: PWORD; Ordinal: DWORD; begin ImageBase := GetModuleHandle('kernel32.DLL'); if ImageBase < HINSTANCE_ERROR then Exit; IID := 0; if PWORD(ImageBase)^ = IMAGE_DOS_SIGNATURE then begin // проверка заголовков pNtHeaders := Pointer(ImageBase + DWORD(PImageDosHeader(ImageBase)^._lfanew)); ExportAddr.VirtualAddress := 0; ExportAddr.Size := 0; if (pNtHeaders^.Signature = IMAGE_NT_SIGNATURE) and (pNtHeaders^.FileHeader.Machine = IMAGE_FILE_MACHINE_I386) then begin // получаем указатель на таблицу экспорта ExportAddr := pNtHeaders.OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_EXPORT]; IID := pNtHeaders.OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + ImageBase; if ExportAddr.VirtualAddress <> 0 then Inc(ExportAddr.VirtualAddress, ImageBase); end; end; if ExportAddr.VirtualAddress = 0 then Exit; IED := PImageExportDirectory(ExportAddr.VirtualAddress); I := 1; NamesCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNames)); OrdinalCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNameOrdinals)); while I < Integer(IED^.NumberOfNames) do begin // получаем имя функции FuncName := PAnsiChar(ImageBase + PDWORD(NamesCursor)^); // Смотрим номер функции в таблице ординалов Ordinal := OrdinalCursor^ + IED^.Base; // Через ординал вычисляем реальный адрес функции FuntionAddr := ImageBase + DWORD(IED^.AddressOfFunctions); FuntionAddr := ImageBase + PDWORD(FuntionAddr + (Ordinal - 1) * 4)^; if (FuntionAddr > ExportAddr.VirtualAddress) and (FuntionAddr <= IID) then Writeln(FuncName, ' forwarded -> ', PAnsiChar(FuntionAddr)) else Writeln(FuncName, ' proc addr: 0x', IntToHex(FuntionAddr, 8)); Inc(I); Inc(NamesCursor); Inc(OrdinalCursor); end; Readln; end.
Тут еще бы учесть что PImageDosHeader(ImageBase)^._lfanew можно нарошно скорректировать как очень большое например FFFF, И все остальные офсеты тоже. Просматривайте чтоб они были в пределах размера образа.
DeniskaV Код (Text): function GetAddr(szDll, szFunc: PAnsiChar): Pointer; var i,j:integer; S:AnsiString; ... begin ... if lstrcmpi(szFunc, PAnsiChar(ImageBase + PDWORD(NamesCursor)^)) = 0 then begin if (FuntionAddr > ExportAddr.VirtualAddress) and //если адрес внутри экспорта, то это forwarded (FuntionAddr < ExportAddr.VirtualAddress+ExportAdr.Size) then begin S:=PAnsiChar(FuntionAddr); //строка вида ntdll.RtlAllocateHeap j:=Pos('.',S); if j > 0 then begin S[j]:=#0; //разбиваем на ntdll и RtlAllocateHeap Result:=GetAddr(@S[1],@S[j+1]); //вызываем эту же функцию для определения адреса end; end else Result:=Pointer(FuntionAddr); Break; //раз уже нашли, то выходим из цикла end; ...
Спасибо, все отлично работает! Еще такой вопрос, если собрать ехешник с использованием этой функции например простой мессаджбокс, все антивирусы молчат. Если собрать в виде криптера, то начинают палить.. Куда копать? В сторону антиэмуляции? Поможет ли если я: вызову такую апи, результаты эмуляции которой будут отличаться от эмуляции в реальной среде, сравню результаты исполнения, если результат как в реальной среде - мой код, если результат отличается - ExitProcess?