Results 1 to 6 of 6

Thread: API functie opzoeken in DLL

  1. #1
    Win32.Trojan.Heur.Herby
    Join Date
    Dec 2003
    Location
    Nuenen of all places
    Posts
    289

    API functie opzoeken in DLL

    Hoi

    Onderstaande code probeer ik werkend te krijgen.

    Mijn orignele functie "GetProcAddressFromDLL1" werkt naar behoren.
    Als voorbeeld probeer ik dan MessageBoxW aan te roepen en dat wordt netjes getoond op het scherm.

    Nu dacht ik, wat als ik nu eerst kijk of een functie (MessageBoxW in dit geval) wel bestaat. (functie "GetProcAddressFromDLL2")
    Nou dat is iets moeilijker dan gedacht.
    Hierbij gebruik ik de uitleg op "exploring-the-export-table"

    Op zich gaat het best aardig, als laatste probeer ik dan de name op te vragen, die wil ik dan vergelijken met de zoekopdracht.
    Indien gelijk dan de ordinal opvragen, procaddress terug geven en klaar.

    Ik loop dan ook vast om een lijst met namen terug te krijgen, wat ik ook probeer, ik krijg alleen troep terug.

    Iemand ervaring hiermee, mijn C kennis is niet al te geweldig?


    Code:
    program ApiTest;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils,
      WinAPI.Windows;
    
    {----------------------------------------------------------------------------------------------------------------------}
    { in Windows.pas                                                                                                       }
    {function MessageBoxW(hWnd: HWND; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall; external user32 name 'MessageBoxW';}
    var
      MessageBoxW : FUNCTION (hWnd:HWND; lpText, lpCaption: LPCWSTR; uType: UINT) : Integer; STDCALL;
    {----------------------------------------------------------------------------------------------------------------------}
    // Direct test, vanuitgaande dat de functie bestaat;
    function GetProcAddressFromDLL1 (aHandle: THandle; aFunctionName: PChar) : Pointer;
    begin
      result := nil;
      if aHandle = 0 then exit;
      if Trim(aFunctionName) = '' then exit;
    
      result := GetProcAddress(aHandle, aFunctionName);
    end;
    {----------------------------------------------------------------------------------------------------------------------}
    // Indirect test, checking if the requested FunctionName exists in the DLL.
    // https://dev.to/wireless90/getting-the-windows-pe-internals-kka
    // Information 32bit structure: https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
    //
    // RVA (relative virtual address)
    //    In an image file, the address of an item after it is loaded into memory, with the base address of the image file
    //    subtracted from it. The RVA of an item almost always differs from its position within the file on disk (file pointer).
    // VA (virtual address)
    //    Same as RVA, except that the base address of the image file is not subtracted. The address is called a “VA” because
    //    Windows creates a distinct VA space for each process, independent of physical memory. For almost all purposes,
    //    a VA should be considered just an address
    
    // Info opgezocht met PPEE (PPEE (puppy) is a Professional PE file Explorer)  https://www.mzrst.com/
    // File: user32.dll | 07-03-2022 19:14 | 1701896 bytes  Windows 10 Pro/x64
    // Base = 000005DE = 1502
    // NumberOfFunctions = 000004BF = 1215
    // NumberOfNames = 000003E6 = 998
    // AddressOfFunctions = 0009D238 = 643640
    // AddressOfNames = 0009E534 = 648500
    // AddressNamesOfNamesOrdinals = 0009F4CC = 652492
    // MessageBoxW | Ordinal $086C = 2156 | RVA = $00080EC0 = 528064 | NameRVA = $000A2B0D = 666381
    
    function GetProcAddressFromDLL2 (aPEbase: HMODULE; aFunctionName: PChar) : Pointer;
    Type
      BytePtr = ^Byte;
      TDWordArray = array[0..0] of DWord;
      PDWordArray = ^TDWordArray;
    
    var
      ImageDosHeader : PImageDosHeader;
      ImageNTHeaders : PImageNTHeaders;
      ImageExportDirectory  : PImageExportDirectory;
      NameIndex : Cardinal;
      Name : PChar;
    begin
      result := nil;
      if aPEbase = 0 then exit;
    
      // PEbase points to IMAGE_DOS_HEADER structure
      ImageDosHeader := pointer(aPEbase);
    
      if NOT assigned (ImageDosHeader) then
        begin
          WriteLn(format('ERR %-35s : $%p (%d)',['ImageDosHeader', ImageDosHeader, cardinal(ImageDosHeader)]));
        end
      else
        begin
          WriteLn(format('OK  %-35s : $%p (%d)',['ImageDosHeader', ImageDosHeader, cardinal(ImageDosHeader)]));
          //Checking for a valid 'MZ' signature
          if NOT ImageDosHeader.e_magic = IMAGE_DOS_SIGNATURE then
            begin
              WriteLn(format('ERR %-35s : $%x (%d)',['ImageDosHeader MZ Signature', ImageDosHeader.e_magic, ImageDosHeader.e_magic]));
            end
          else
            begin
              WriteLn(format('OK  %-35s : $%x (%d)',['ImageDosHeader MZ Signature', ImageDosHeader.e_magic, ImageDosHeader.e_magic]));
    
              // Checking for a valid PE signature in the ImageDosHeader
              // _lfanew contains a RVA whose offset leads to the PImageNTHeaders structure
              // lfanew = long file address of the new executable header
              // To compute the destination (RVA), take Base Address (PEbase) and use the lfnew as offset
    
              ImageNTHeaders     := Pointer(dword(aPEbase) + dword(ImageDosHeader._lfanew));
              if NOT assigned(ImageNTHeaders) then
                begin
                  WriteLn(format('ERR %-35s : $%p (%d)',['ImageNTHeaders', ImageNTHeaders, cardinal(ImageNTHeaders)]));
                end
              else
                begin
                  WriteLn(format('OK  %-35s : $%p (%d)',['ImageNTHeaders', ImageNTHeaders, cardinal(ImageNTHeaders)]));
                  //Checking for a valid 'PE' signature
                  if NOT ImageNTHeaders.Signature = IMAGE_NT_SIGNATURE then
                    begin
                      WriteLn(format('ERR %-35s : $%x (%d)',['ImageNTHeaders PE Signature', ImageNTHeaders.Signature, ImageNTHeaders.Signature]));
                    end
                  else
                    begin
                      WriteLn(format('OK  %-35s : $%x (%d)',['ImageNTHeaders PE Signature', ImageNTHeaders.Signature, ImageNTHeaders.Signature]));
                      // Get RVA for the 'ExportTable Data Directory' from the OptionalHeader DataDirectory Virtual Adress. RVA = PEbase + Virtual Address
                      ImageExportDirectory  := Pointer(ImageNTHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + cardinal(aPEbase));
                      if NOT assigned(ImageExportDirectory) then
                        begin
                          WriteLn(format('ERR %-35s : $%p (%d)',['ImageExportDirectory', ImageExportDirectory, cardinal(ImageExportDirectory)]));
                        end
                      else
                        begin
                          WriteLn(format('OK  %-35s : $%p (%d)',['ImageExportDirectory', ImageExportDirectory, cardinal(ImageExportDirectory)]));
    
                          // Get ExportAddressTable, NameOrdinalsArray and NameAddressArray
    
    
    //                        https://dev.to/wireless90/exploring-the-export-table-windows-pe-internals-4l47 (voorbeeld)
    //
    //                        int nameIndex = 0;
    //                        for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
    //                        {
    //                             char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
    //                             if (strcmp("MessageBoxA", name) == 0)
    //                             {
    //                                    WORD ordinal = nameOrdinalsPointer[nameIndex];
    //                                    PDWORD targetFunctionAddress = (PDWORD)((unsigned char*)peBase + exportAddressTable[ordinal]);
    //                             }
    //                        }
    
    
                          NameIndex := 0;
                          repeat            //
                            Name := PChar (  aPEbase + cardinal(ImageExportDirectory.AddressOfNames) + NameIndex);  //<-------- ISSUE
    
    
                            //--> TODO if Name = aFunctionName then result := GetProcAddress(aPEbase, aFunctionName);
                            inc(NameIndex);
                          until NameIndex  ImageExportDirectory.NumberOfNames;
    
                        end;
                    end;
                end;
            end;
        end;
    end;
    
    {----------------------------------------------------------------------------------------------------------------------}
    
    
    
    
    var
      DLLHandle   : THandle = 0;
    begin
      try
        DLLHandle := LoadLibrary ('user32.dll');
        try
          {
          // Werkt
          MessageBoxW := nil;
          MessageBoxW := GetProcAddressFromDLL1(DLLHandle,'MessageBoxW');
          if assigned(MessageboxW) then MessageBoxW(0,'Test 1 - Direct','Direct API Call',0);
          }
    
          // Werkt NIET
          MessageBoxW := nil;
          MessageBoxW := GetProcAddressFromDLL2(DLLHandle,'MessageBoxW');
          if assigned(MessageboxW) then MessageBoxW(0,'Test 2 - Indirect','Indirect API Call',0);
    
    
        finally
          FreeLibrary(DLLHandle);
        end;
    
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      readln;
    end.

  2. #2
    Win32.Trojan.Heur.Herby
    Join Date
    Dec 2003
    Location
    Nuenen of all places
    Posts
    289
    Tja..... gewoon AnsiChar dus :-(

  3. #3
    Waarom zou je dat willen? GetProcAddress, en daarmee GetProcAddressFromDLL1, geeft toch gewoon nil als de proc niet bestaat? Ze zijn beide dynamische aanroepen. Als het alleen is om de internals te begrijpen, dan snap ik het, maar ik zie verder niet het functionele voordeel.
    1+1=b

  4. #4
    Ik zag het voordeel ook niet zo.

    Op stackoverflow staat een voorbeeld om een lijst te krijgen uit een willekeurige dll zonder deze eerste te laden met LoadLibrary. Daar zou ik dan misschien wel een voordeel in kunnen zien. Maar eerst LoadLibrary en dan dit... (?)

  5. #5
    Win32.Trojan.Heur.Herby
    Join Date
    Dec 2003
    Location
    Nuenen of all places
    Posts
    289
    Quote Originally Posted by GolezTrol View Post
    Waarom zou je dat willen? GetProcAddress, en daarmee GetProcAddressFromDLL1, geeft toch gewoon nil als de proc niet bestaat? Ze zijn beide dynamische aanroepen. Als het alleen is om de internals te begrijpen, dan snap ik het, maar ik zie verder niet het functionele voordeel.
    Haha, nee ik was aan het knutselen en vroeg me af wat als ik wil weten of iets bestaat, het was meer dat ik wilde weten hoe iets werkt.
    Ik weet dat met GetProcAddress op nil ik het juiste resultaat zou hebben, ik wilde gewoon eens kijken hoe en wat.
    Uiteindelijk gebruik ik het niet, maar ik snap nu wat meer hoe het met DLL's werkt.

    Misschien had ik dat vooraf even moeten vermelden :-)

  6. #6
    Win32.Trojan.Heur.Herby
    Join Date
    Dec 2003
    Location
    Nuenen of all places
    Posts
    289
    Quote Originally Posted by rvk View Post
    Ik zag het voordeel ook niet zo.

    Op stackoverflow staat een voorbeeld om een lijst te krijgen uit een willekeurige dll zonder deze eerste te laden met LoadLibrary. Daar zou ik dan misschien wel een voordeel in kunnen zien. Maar eerst LoadLibrary en dan dit... (?)
    Ik had al wat voorbeeldjes gevonden, ik wilde gewoon eens uitvogelen hoe zoiets nu in elkaar zit, ik gebruik zelden direct DLL's.
    Was een leuk knutsel projectje wat nu ergens in de hoek ligt.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •