Search code examples
c#dllimportkernel32

Programmatically reading callable dll functions


My project needs to inspect .c and .dll files. it combines those informations to determine what it is supposed to call and then calls that.

I need to inspect the dlls to find which dll has which function. I have gotten so far as to map the dll to memory without initialising it. now i need to map the header to something so i can read out the section that has the callable names in it.

how can i do that? this is the code so far:

    [DllImport("kernel32.dll")]
    static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

    public static string[] GetFKTNames(string dll)
    {
        IntPtr lib = LoadLibraryEx(dll, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);

        //INDICATES WHAT I WANT TO DO, BUT DOES NOT WORk
        //Header header = GetHeader(lib);
        //Unload(lib);
        //return header.Names;
}

EDIT #2:

i made a little bit of progress and quit it for today... there are 4 free days coming up here in germany...

i am not entirely sure if that marshalling is correct - i had no way to test it. I would love to read a book on that topic - so please comment if you know a good book that explains how that headerstuff works and what different headers are out there.

    private static List<string> ListDLLFunctions(string sADllName)
    {
        List<string> names = new List<string>();
        IntPtr LoadedImage = LoadLibraryEx(sADllName, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);

        IMAGE_NT_HEADERS header = (IMAGE_NT_HEADERS) Marshal.PtrToStructure(libPtr, typeof(IMAGE_NT_HEADERS));

        //    ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)
        //        ImageDirectoryEntryToData(LoadedImage.MappedAddress,
        //        false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
        //    if (ImageExportDirectory != NULL)
        //    {
        //        dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader, 
        //            LoadedImage.MappedAddress,
        //        ImageExportDirectory->AddressOfNames, NULL);
        //        for(size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
        //        {
        //            sName = (char *)ImageRvaToVa(LoadedImage.FileHeader, 
        //                    LoadedImage.MappedAddress,
        //                   dNameRVAs[i], NULL);
        //         slListOfDllFunctions.push_back(sName);
        //        }
        //    }
        FreeLibrary(LoadedImage);
        return names;
    }

    static void Main(string[] args)
    {
        List<string> names = ListDLLFunctions("KERNEL32.DLL");
    }

Solution

  • I solved the problem for my usecase:

    I load the lib into momory, copy it into a bytearray and then investigate it using PE information.

    There are a couple of useful ressources for this topic that helped me a lot:

    In retrospec i get how i could have used the other approaches to get my information. The Class that i have written is great to learn PE and explore some dlls, i recommand rewriting it if you want to get the grip of how the PE works.

    Here is the Class:

    public class DLLHelper
    {
        private byte[] dllInMemory;
        private UInt32 PESizeOfImage;
        private UInt32 VA_PE;
    
        [DllImport("kernel32.dll")]
        static public extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
    
        [DllImport("kernel32.dll")]
        static public extern bool FreeLibrary(IntPtr hModule);
    
        public enum LoadLibraryFlags : uint
        {
            DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
            LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
            LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
            LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
            LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
            LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
        }
    
    
        public DLLHelper(string dllpath)
        {
            PESizeOfImage = GetDllSizeInMemory(dllpath);
            dllInMemory = GetDLLCopy(dllpath, PESizeOfImage);
            UInt32 VA_p_PE = 0x3C;
            VA_PE = Get4ByteFromLocation(VA_p_PE, dllInMemory);
        }
    
        private byte[] GetDLLCopy(string dllpath, uint PESizeOfImage)
        {
            IntPtr libPtr = LoadLibraryEx(dllpath, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
            byte[] dllInMemory = new byte[PESizeOfImage];
            Marshal.Copy(libPtr, dllInMemory, 0, (int)PESizeOfImage);
            FreeLibrary(libPtr);
            return dllInMemory;
        }
    
        private UInt32 GetDllSizeInMemory(string dllpath)
        {
            byte[] dllpreload = File.ReadAllBytes(dllpath);
            UInt32 pp_PE = 0x3C;
            UInt32 p_PE = Get4ByteFromLocation(pp_PE, dllpreload);
            UInt32 p_PEOPTIONALHEADER = p_PE + 0x18;
            UInt32 p_PESizeOfImage = p_PEOPTIONALHEADER + 0x38;
            return Get4ByteFromLocation(p_PESizeOfImage, dllpreload);
        }
    
        public void DumpToFile(String filename)
        {
            File.WriteAllBytes(filename, dllInMemory);
        }
    
        public string GetDLLName()
        {
            UInt32 VAExport = GetVAExport(VA_PE, dllInMemory);
            UInt32 VAName = GetVAName(VAExport, dllInMemory);
            String Name = GetString(VAName, dllInMemory);
            return Name;
        }
    
        public List<String> GetFunctionNames()
        {
            List<String> fkts = new List<String>();
            UInt32 VAExport = GetVAExport(VA_PE, dllInMemory);
            UInt32 VA_p_firstFKT = GetVA_p_firstFKT(VAExport, dllInMemory);
            UInt32 VA_p_lastFKT = GetVA_p_lastFKT(VAExport, dllInMemory);
            for (UInt32 VA_p_fkt = VA_p_firstFKT; VA_p_fkt <= VA_p_lastFKT; VA_p_fkt += sizeof(UInt32))
            {
                UInt32 VA_fkt = Get4ByteFromLocation(VA_p_fkt, dllInMemory);
                fkts.Add(GetString(VA_fkt, dllInMemory));
            }
            return fkts;
        }
    
        private UInt32 GetVA_p_lastFKT(UInt32 VAExport, byte[] dllInMemory)
        {
            UInt32 first = GetVA_p_firstFKT(VAExport, dllInMemory);
            UInt32 count = GetfktCount(VAExport, dllInMemory);
            UInt32 last = first + (count - 1) * sizeof(UInt32);
            return last;
        }
    
        private UInt32 GetfktCount(UInt32 VAExport, byte[] dllInMemory)
        {
            UInt32 RVA_Count = 0x14;
            UInt32 VA_Count = VAExport + RVA_Count;
            return Get4ByteFromLocation(VA_Count, dllInMemory);
        }
    
        private UInt32 GetVA_p_firstFKT(UInt32 VAExport, byte[] dllInMemory)
        {
            UInt32 RVA_p_FIRST = 0x20;
            UInt32 VA_p_FIRST = VAExport + RVA_p_FIRST;
            return Get4ByteFromLocation(VA_p_FIRST, dllInMemory);
        }
    
        private UInt32 GetVAName(UInt32 VAExport, byte[] dllInMemory)
        {
            UInt32 RVA_p_NAME = 0x0C;
            UInt32 VA_p_NAME = VAExport + RVA_p_NAME;
            return Get4ByteFromLocation(VA_p_NAME, dllInMemory);
        }
    
        private UInt32 GetVAExport(UInt32 VAPE, byte[] dllInMemory)
        {
            UInt32 RVA_p_EXPORT = 0x78;
            UInt32 VA_p_EXPORT = VAPE + RVA_p_EXPORT;
            return Get4ByteFromLocation(VA_p_EXPORT, dllInMemory);
        }
    
       string GetString(UInt32 location, byte[] dll)
        {
            int length = 0;
            while (dll[location + length] != 0x00)
            {
                length++;
            }
            if (location > int.MaxValue) throw new Exception("uncastable");
            return Encoding.UTF8.GetString(dll, (int)location, length);
        }
    
       private UInt32 Get4ByteFromLocation(UInt32 location, byte[] dll)
       {
           if (!(BitConverter.IsLittleEndian))
           {
               byte[] partial = GetByteSubset(4, location, dll);
               Array.Reverse(partial);
               return BitConverter.ToUInt32(partial, 0);
           }
           return BitConverter.ToUInt32(dll, (int)location);
       }
    
       private byte[] GetByteSubset(int size, UInt32 location, byte[] dll)
       {
           byte[] val = new byte[size];
           for (int i = 0; i < size; i++)
           {
               val[i] = dll[location + i];
           }
           return val;
       }
    }