Cheatsheet - Get Exported Function Address by Function Name

First in possibly a series of quick reviews of common activities seen or used in malware analysis. Most of these will probably have been documented else where online; however, recording them here for my own edification.

Synopsis

This cheatsheet will make use of x64Dbg to walk a PE file in memory to acquire the address of a function based upon its name. The goal is to be familiar with how malware can do the same thing.


### Cheatsheet

The formula variables will need to be replaced with values deduced at run time.

Formulas

Formulas are meant for use in x64Dbg's calculator and all values are in hex.

Formula 1 - Find Index of Function Name
AddressBase + ([AddressBase + [AddressBase + [(AddressBase + [AddressBase + 3c]) + 78] + 20] + (4 * Index)])

Formula 2 - Retrieve Address Of Function
AddressBase + ([AddressBase + [AddressBase + [(AddressBase + [AddressBase + 3c]) + 78] + 1C] + (4*(word(AddressBase + [AddressBase + [(AddressBase + [AddressBase + 3c]) + 78] + 24] + (2 * Index))))])

Variables

  • AddressBase: The base memory address the PE file was loaded into
  • Index: The zero based index into AddressOfNames/AddressOfNameOrdinals array

### Formulas Explained > The formulas here are constructed based upon the explanation from "Iczelion's PE Tutorial 7"[^n].

The above functions work nicely/only in x64Dbg's calculator. Let's break these down, starting with the smaller parts and building outward.

First retrieve the address of the PE signature. The RVA to the PE header is located at offset 0x3C from the start of the file.

The brackets are important as they return the value stored at that memory location versus returning the memory location.

[AddressBase + 3c]

Next, building upon that, retrieve the address of the IMAGE_EXPORT_DIRECTORY[^n]. Offset 0x78 from the PE signature contains the first IMAGE_DATA_DIRECTORY entry. The first entry, conveniently, contains the RVA to the IMAGE_EXPORT_DIRECTORY.

[(AddressBase + [AddressBase + 3c]) + 78]

These smaller formulas are embedded in both larger formulas. Basically they provide the locations needed to get to the `IMAGE_EXPORT_DIRECTORY` from which everything else needed is found.
#### Find Index of Function Name For this formula, we make use of the `AddressOfNames` field found in the `IMAGE_EXPORT_DIRECTORY` struct.

The offset into IMAGE_EXPORT_DIRECTORY that contains the AddressOfNames RVA is 0x20

AddressBase + 
[
   AddressBase + 
   [
      (
         AddressBase + 
         [
            AddressBase + 3C
         ]
      )
      + 78
   ]
   + 20
]

Each value in the array is a dword (4 bytes in length), so to iterate through the list of name we add (4 * Index).

[
   AddressBase + 
   [
      AddressBase + 
      [
         (
            AddressBase + 
            [
               AddressBase + 3c
            ]
         )
         + 78
      ] 
      + 20
   ]
   + 
   (
      4 * Index
   )
]

The above formula gives us the RVA to the string's location which needs to be added to the AddressBase to get the actual location. Add the adjustment to complete the formula.

AddressBase + 
(
   [
      AddressBase + 
      [
         AddressBase + 
         [
            (
               AddressBase + 
               [
                  AddressBase + 3c
               ]
            )
            + 78
         ]
         + 20
      ]
      +
      (
         4 * Index
      )
   ]
)

#### Retrieve Address Of Function This function does a few things. Going to explain the parts and then put them all together.
  1. Get the index value from AddressOfNameOrdinals
  • Purpose: Retrieve the value stored here as it's the index into the AddressOfFunctions where the RVA to the function's address resides.

  • IMAGE_EXPORT_DIRECTORY Offset: 0x24

  • Size of each entry: word

  • Index : Set this to match the index used to look up the name in the AddressOfNames (Formula 1).

        word
        (
           AddressBase + 
           [
              AddressBase + 
              [
                 (
                    AddressBase + 
                    [
                       AddressBase + 3c
                    ]
                 )
                 + 78
              ]
              + 24
           ]
           +
           (
              2 * Index
           )
        )
    
  1. Get the location of the AddressOfFunctions array
  • Purpose: Get the location of the AddressOfFunctions so the array of function RVAs can be accessed

  • Offset in IMAGE_EXPORT_DIRECTORY: 0x1C

  • Size of each entry: dword

        AddressBase + 
        [
           AddressBase + 
           [   
              (
                 AddressBase + 
                 [
                    AddressBase + 3C
                 ]
              ) 
              + 78
           ]
           + 1C
        ]
    
  1. Putting this all together will construct the formula to acquire the RVA to the actual function. The pseudo-code will look something like this:

     (
        AddressOfFunctions Location
     )
     + 
        (
           (
              Size of each Address Entry
           )
           * 
           (
              Value from AddressOfOrdinals at specified Index
           )
        )
    
  2. That gives us the pointer to the RVA of the function, now we need to de-reference it and add the AddressBase to the value. The final formula does all of this.

     AddressBase + 
     (
        [
           AddressBase + 
           [
              AddressBase + 
              [
                 (
                    AddressBase + 
                    [
                       AddressBase + 3c
                    ]
                 )
                 + 78
              ]
              + 1C
           ]
           +
           (
              4 *
              (
                 word
                 (
                    AddressBase + 
                    [
                       AddressBase + 
                       [
                          (
                             AddressBase + 
                             [
                                AddressBase + 3c
                             ]
                          )
                          + 78
                       ]
                       + 24
                    ]
                    +
                    (
                       2 * Index
                    )
                 )
              )
           )
        ]
     )
    

Example

Before proving our formulas to work, let's identify the function we want to find using these two formulas.

Dll: oleaut32.dll
Function Name: BSTR_UserSize

Look this up in x64Dbg's Symbols tab to get the actual function address along with the base address (Yours will differ).


Screenshot 1 - Function Name and Address to Lookup

First fill out the Find Index of Function Name formula to start walking the array of function names using the calculator in x64Dbg. Remember to use x64Dbg's Follow in Dump option each time to see the ASCII text.

77D40000 + ([77D40000 + [77D40000 + [(77D40000 + [77D40000 + 3c]) + 78] + 20] + (4 * 2)])

Upon the third try (index = 2) we find the droid function we were looking for.


Screenshot 2 - Function Name in Dump

Now that we know the index is 2, we can fill out the second formula in x64Dbg's calculator and click Follow in Disassembler

77D40000 + ([77D40000 + [77D40000 + [(77D40000 + [77D40000 + 3c]) + 78] + 1C] + (4*(word(77D40000 + [77D40000 + [(77D40000 + [77D40000 + 3c]) + 78] + 24] + (2 * 2))))])


Screenshot 3 - Function Address Resolved in Disassembler

Notice that the function address and name shown in the disassembler matches the address we looked up under the Symbols tab in x64Dbg.


## Conclusion And there we have it! Knowing the PE offsets/constants used here will greatly aid in identifying when malware resolves exported function addresses.
## Additional Notes I've created a XMind document outlining relevant PE file offsets[^n]. Also there is one more field of interest we did not discuss in this article: `NumberOfNames`.
### NumberOfNames When walking the `AddressOfNames`, the max number of names stored in that array is kept in the `IMAGE_EXPORT_DIRECTORY` at offset: `0x18`. The field name in the struct is `NumberOfNames` Remember this when walking the `AddressOfNames` to ensure you stay in bounds.
## References [^1]: http://win32assembly.programminghorizon.com/pe-tut7.html [^2]: http://source.winehq.org/source/include/winnt.h#2998 [^3]: https://bitbucket.org/XOR_Hex/xminddocs/src
comments powered by Disqus