Аналог GetProcAddress

Тема в разделе "WASM.BEGINNERS", создана пользователем DeniskaV, 12 дек 2011.

  1. DeniskaV

    DeniskaV New Member

    Публикаций:
    0
    Регистрация:
    6 дек 2011
    Сообщения:
    6
    Приветствую!
    Есть функция:

    Код (Text):
    1. procedure ListDLLExports(FileName: String);
    2. type
    3.   TDWordArray = array [0..$FFFFF] of DWORD;
    4. var
    5.   Name: String;
    6.   i, dirsize: Cardinal;
    7.   imageinfo: LoadedImage;
    8.   pNameRVAs: ^TDWordArray;
    9.   pDummy: PImageSectionHeader;
    10.   pExportDirectory: PImageExportDirectory;
    11. begin
    12.   if MapAndLoad(PChar(FileName), nil, @imageinfo, True, True) then
    13.   begin
    14.     try
    15.       pExportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress,
    16.                                                     False,
    17.                                                     IMAGE_DIRECTORY_ENTRY_EXPORT,
    18.                                                     dirsize);
    19.       if (pExportDirectory <> nil) then
    20.       begin
    21.         pNameRVAs := ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
    22.                                   DWORD(pExportDirectory^.AddressOfNames),
    23.                                   pDummy);
    24.         for i := 0 to pExportDirectory^.NumberOfNames - 1 do
    25.         begin
    26.           Name := PChar(ImageRvaToVa(imageinfo.FileHeader,
    27.                                      imageinfo.MappedAddress,
    28.                                      pNameRVAs^[i], pDummy));
    29.  
    30.           Show(Name);
    31.         end;
    32.       end;
    33.     finally
    34.       UnMapAndLoad(@imageinfo);
    35.     end;
    36.   end;
    37. end;
    Показывает список экспортируемых функций у длл.
    Хочу переписать ее в таком виде:
    function ListDLLExports(FileName, ProcName: String): Pointer;
    Чтобы результат ее выполнения был такой же как у GetProcAddress.

    Основная сложность: Не знаю откуда вытащить адресс функции.
    Предполагаю что из этой структуры:
    Код (Text):
    1.  PImageExportDirectory = ^TImageExportDirectory;
    2.   _IMAGE_EXPORT_DIRECTORY = packed record
    3.       Characteristics: DWord;
    4.       TimeDateStamp: DWord;
    5.       MajorVersion: Word;
    6.       MinorVersion: Word;
    7.       Name: DWord;
    8.       Base: DWord;
    9.       NumberOfFunctions: DWord;
    10.       NumberOfNames: DWord;
    11.       AddressOfFunctions: ^PDWORD;
    12.       AddressOfNames: ^PDWORD;
    13.       AddressOfNameOrdinals: ^PWord;
    14.   end;
    Подскажите пожалуйста, как это сделать :)
     
  2. valterg

    valterg Active Member

    Публикаций:
    0
    Регистрация:
    19 авг 2004
    Сообщения:
    2.105
    Твой исходник работает с файлом. Указатель на функцию появляется после загрузки файла DLL в память. Поэтому никакие манипуляции с файлом не помогут тебе получить то, что совсем в другом месте хранится. Если бы все так просто было, народ бы не обсуждал до посинения как скрытым образом вызвать GetProcAddress.
     
  3. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    DeniskaV
    В том же цикле чтения имен читаешь значение ординала функции из массива AddressOfNameOrdinals, вычитаешь из него значение Base и по полученному индексу читаешь RVA из массива AddressOfFunctions.
    Однако полученный адрес м.б. ссылкой на имя функции из другой dll (forwarded экспорт), поэтому нужно еще проверить - если полученный адрес лежит за пределами таблицы экспорта, то это реальный адрес экспортируемой функции, если же внутри, то это указатель на составное имя - например, в kernel32 адрес ф-ии HeapAlloc даст ссылку не на саму ф-ю, а на строку "ntdll.RtlAllocateHeap"

    PS: Если ты грузишь dll не через LoadLibrary, а просто мапишь dll из файла, то имеет смысл получать только RVA функций
     
  4. DeniskaV

    DeniskaV New Member

    Публикаций:
    0
    Регистрация:
    6 дек 2011
    Сообщения:
    6
    Спасибо! Нашел решение:
    Код (Text):
    1. function GetAddr(szDll, szFunc: PChar): Pointer;
    2. var
    3.   pNtHeaders: PImageNtHeaders;
    4.   ExportAddr: TImageDataDirectory;
    5.   ImageBase: DWORD;
    6.   IED: PImageExportDirectory;
    7.   i: Integer;
    8.   FuntionAddr: DWORD;
    9.   NamesCursor: PDWORD;
    10.   OrdinalCursor: PWORD;
    11.   Ordinal: DWORD;
    12. begin
    13.   Result := nil;
    14.   ZeroMemory(@ExportAddr, SizeOf(ExportAddr));
    15.   ImageBase := GetModuleHandle(szDll);
    16.   if PWORD(ImageBase)^ = IMAGE_DOS_SIGNATURE then
    17.   begin
    18.     pNtHeaders := Pointer(ImageBase + DWORD(PImageDosHeader(ImageBase)^._lfanew));
    19.     if (pNtHeaders^.Signature = IMAGE_NT_SIGNATURE) and
    20.        (pNtHeaders^.FileHeader.Machine = IMAGE_FILE_MACHINE_I386) then
    21.     begin
    22.       ExportAddr := pNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    23.       if ExportAddr.VirtualAddress <> 0 then Inc(ExportAddr.VirtualAddress, ImageBase)
    24.       else ExportAddr.Size := 0;
    25.     end;
    26.   end;
    27.   if (ImageBase = 0) or (ExportAddr.VirtualAddress = 0) then Exit;
    28.   IED := PImageExportDirectory(ExportAddr.VirtualAddress);
    29.   i := 1;
    30.   NamesCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNames));
    31.   OrdinalCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNameOrdinals));
    32.   while i < Integer(IED^.NumberOfNames) do
    33.   begin
    34.     Ordinal := OrdinalCursor^ + IED^.Base;
    35.     FuntionAddr := ImageBase + DWORD(IED^.AddressOfFunctions);
    36.     FuntionAddr := ImageBase + PDWORD(FuntionAddr + (Ordinal - 1) * 4)^;
    37.     if lstrcmpi(szFunc, PAnsiChar(ImageBase + PDWORD(NamesCursor)^)) = 0 then
    38.        Result := Pointer(FuntionAddr);
    39.     Inc(i);
    40.     Inc(NamesCursor);
    41.     Inc(OrdinalCursor);
    42.   end;
    43. end;
     
  5. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    DeniskaV
    А если функция первая/вторая в списке? ... Break забыл влепить!
    Да и вообще нужна проверка значения FuntionAddr на указание внутрь массива (выше leo говорил о forwarded экспорте).
     
  6. DeniskaV

    DeniskaV New Member

    Публикаций:
    0
    Регистрация:
    6 дек 2011
    Сообщения:
    6
    Ммм.. Подскажите плз как это сделать, я в этом плохо разбираюсь :dntknw:
    Break Нужно там где: Result := Pointer(FuntionAddr); ???
     
  7. DeniskaV

    DeniskaV New Member

    Публикаций:
    0
    Регистрация:
    6 дек 2011
    Сообщения:
    6
    Возможно кому то тоже пригодится, код с учетом:
    > например, в kernel32 адрес ф-ии HeapAlloc даст ссылку не
    > на саму ф-ю, а на строку "ntdll.RtlAllocateHeap"

    Код (Text):
    1. program Project1;
    2.  
    3. {$APPTYPE CONSOLE}
    4.  
    5. uses
    6.   Windows,
    7.   SysUtils;
    8.  
    9. var
    10.   FuncName: AnsiString;
    11.   pNtHeaders: PImageNtHeaders;
    12.   ExportAddr: TImageDataDirectory = (VirtualAddress: 0; Size: 0);
    13.   ImageBase: DWORD;
    14.   IED: PImageExportDirectory;
    15.   IID: DWORD;
    16.   I: Integer;
    17.   FuntionAddr: DWORD;
    18.   NamesCursor: PDWORD;
    19.   OrdinalCursor: PWORD;
    20.   Ordinal: DWORD;
    21. begin
    22.   ImageBase := GetModuleHandle('kernel32.DLL');
    23.   if ImageBase < HINSTANCE_ERROR then Exit;
    24.  
    25.   IID := 0;
    26.  
    27.   if PWORD(ImageBase)^ = IMAGE_DOS_SIGNATURE then
    28.   begin
    29.     // проверка заголовков
    30.     pNtHeaders := Pointer(ImageBase + DWORD(PImageDosHeader(ImageBase)^._lfanew));
    31.     ExportAddr.VirtualAddress := 0;
    32.     ExportAddr.Size := 0;
    33.     if (pNtHeaders^.Signature = IMAGE_NT_SIGNATURE) and
    34.       (pNtHeaders^.FileHeader.Machine = IMAGE_FILE_MACHINE_I386) then
    35.     begin
    36.       // получаем указатель на таблицу экспорта
    37.       ExportAddr := pNtHeaders.OptionalHeader.DataDirectory
    38.         [IMAGE_DIRECTORY_ENTRY_EXPORT];
    39.       IID :=
    40.         pNtHeaders.OptionalHeader.DataDirectory[
    41.           IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + ImageBase;
    42.       if ExportAddr.VirtualAddress <> 0 then
    43.         Inc(ExportAddr.VirtualAddress, ImageBase);
    44.     end;
    45.   end;
    46.  
    47.   if ExportAddr.VirtualAddress = 0 then Exit;
    48.  
    49.   IED := PImageExportDirectory(ExportAddr.VirtualAddress);
    50.   I := 1;
    51.   NamesCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNames));
    52.   OrdinalCursor := Pointer(ImageBase + DWORD(IED^.AddressOfNameOrdinals));
    53.   while I < Integer(IED^.NumberOfNames) do
    54.   begin
    55.     // получаем имя функции
    56.     FuncName := PAnsiChar(ImageBase + PDWORD(NamesCursor)^);
    57.     // Смотрим номер функции в таблице ординалов
    58.     Ordinal := OrdinalCursor^ + IED^.Base;
    59.     // Через ординал вычисляем реальный адрес функции
    60.     FuntionAddr := ImageBase + DWORD(IED^.AddressOfFunctions);
    61.     FuntionAddr := ImageBase + PDWORD(FuntionAddr + (Ordinal - 1) * 4)^;
    62.     if (FuntionAddr > ExportAddr.VirtualAddress) and (FuntionAddr <= IID) then
    63.       Writeln(FuncName, ' forwarded -> ', PAnsiChar(FuntionAddr))
    64.     else
    65.       Writeln(FuncName, ' proc addr: 0x', IntToHex(FuntionAddr, 8));
    66.     Inc(I);
    67.     Inc(NamesCursor);
    68.     Inc(OrdinalCursor);
    69.   end;
    70.   Readln;
    71.  
    72. end.
     
  8. XshStasX

    XshStasX New Member

    Публикаций:
    0
    Регистрация:
    9 авг 2008
    Сообщения:
    991
    Тут еще бы учесть что PImageDosHeader(ImageBase)^._lfanew можно нарошно скорректировать как очень большое например FFFF,
    И все остальные офсеты тоже. Просматривайте чтоб они были в пределах размера образа.
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    DeniskaV
    Код (Text):
    1. function GetAddr(szDll, szFunc: PAnsiChar): Pointer;
    2. var
    3.   i,j:integer;
    4.   S:AnsiString;
    5.   ...
    6. begin
    7.   ...
    8.   if lstrcmpi(szFunc, PAnsiChar(ImageBase + PDWORD(NamesCursor)^)) = 0 then
    9.   begin
    10.     if (FuntionAddr > ExportAddr.VirtualAddress) and //если адрес внутри экспорта, то это forwarded
    11.        (FuntionAddr < ExportAddr.VirtualAddress+ExportAdr.Size) then
    12.     begin
    13.       S:=PAnsiChar(FuntionAddr); //строка вида ntdll.RtlAllocateHeap
    14.       j:=Pos('.',S);
    15.       if j > 0 then
    16.       begin
    17.         S[j]:=#0; //разбиваем на ntdll и RtlAllocateHeap
    18.         Result:=GetAddr(@S[1],@S[j+1]); //вызываем эту же функцию для определения адреса
    19.       end;
    20.     end
    21.     else
    22.       Result:=Pointer(FuntionAddr);
    23.     Break; //раз уже нашли, то выходим из цикла
    24.   end;
    25.   ...
     
  10. DeniskaV

    DeniskaV New Member

    Публикаций:
    0
    Регистрация:
    6 дек 2011
    Сообщения:
    6
    Спасибо, все отлично работает! :)

    Еще такой вопрос, если собрать ехешник с использованием этой функции например простой мессаджбокс, все антивирусы молчат.
    Если собрать в виде криптера, то начинают палить.. Куда копать? В сторону антиэмуляции? Поможет ли если я:
    вызову такую апи, результаты эмуляции которой будут отличаться от эмуляции в реальной среде, сравню результаты исполнения, если результат как в реальной среде - мой код, если результат отличается - ExitProcess?