Click here to Skip to main content
Click here to Skip to main content

Tagged as

Checking for exported symbols/functions in a DLL without loading it

, 19 Dec 2010
Rate this:
Please Sign up or sign in to vote.
You can find out if a DLL exports some symbols/functions without calling LoadLibrary() on it
The tip/trick title speaks for itself. I give you a simple function that takes a file name and a list of symbols/function names, and it returns true if the specified file is a DLL that exports all of the specified symbols. Detecting exports by ordinal numbers are not supported because I wasn't in need of that, but if someone needs it and I have the time, then I can put it in easily because it's easier to check than the exports by names. All this is done without loading the DLL, thus avoiding execution of a possibly dangerous DllMain() of an unwanted DLL in your process. It works with both 32 and 64 bit binaries. I used it in a plugin system to safely detect some plugins of older versions in a program. Use this code only when reasonable because checking all DLLs with this before all LoadLibrary() calls can slow down your plugin initialization code in a system with lot of plugins (eg.: GIMP)!!!
That's all folks! Big Grin | :-D
 
stdafx.h:
#pragma once
 
#include <windows.h>
#include <winnt.h> // must be after windows.h
#include <vector>
#include <set>
#include <string>
#include <cassert>
 
stdafx.cpp:
#include "stdafx.h"
 
CheckForDllExportedSymbols.h:
#ifndef __CHECK_FOR_DLL_EXPORTED_SYMBOLS_H__
#define __CHECK_FOR_DLL_EXPORTED_SYMBOLS_H__
#pragma once
 
enum ECFESResult
{
    eCFES_OK,
    eCFES_MissingFunctions,
    eCFES_ErrorOpeningFile,
    eCFES_ErrorReadingFile,
    eCFES_InvalidDosHeader,
    eCFES_InvalidNTHeader,
    eCFES_NotDLL,
    eCFES_DLLStructureError,
};
 
// Checks if the specified file is a real DLL that exports all 
// the specified functions.
ECFESResult CheckForExportedSymbols(LPCTSTR dll_path, 
    const char* symbols[], int symbol_count);
 
#endif
 
CheckForDllExportedSymbols.cpp:
#include "stdafx.h"
#include "CheckForDllExportedSymbols.h"

struct SRVAToFileOffset : public std::vector<IMAGE_SECTION_HEADER>
{
    DWORD operator()(DWORD RVA) const
    {
        for (const_iterator it=begin(), eit = end(); it != eit; ++it)
        {
            if (RVA >= it->VirtualAddress && RVA < it->VirtualAddress + 
    	        it->Misc.VirtualSize)
            return RVA - it->VirtualAddress + it->PointerToRawData;
        }
 
        return RVA;
    }
};
 
//-----------------------------------------------------------------------------

class CCFESFile
{
    public:
    CCFESFile() : m_hFile(INVALID_HANDLE_VALUE) {}
 
    ~CCFESFile()
    {
        Close();
    }
 
    bool OpenForRead(LPCTSTR file_path)
    {
        Close();
        m_hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, 
            OPEN_EXISTING, 0, NULL);
        return m_hFile != INVALID_HANDLE_VALUE;
    }
 
    void Close()
    {
        if (m_hFile != INVALID_HANDLE_VALUE)
        {
            ::CloseHandle(m_hFile);
            m_hFile = INVALID_HANDLE_VALUE;
        }
    }
 
    bool Read(void* buffer, DWORD bytes_to_read, 
        DWORD file_offset = 0xFFFFFFFF, DWORD* bytes_read = NULL)
    {
        assert(m_hFile != INVALID_HANDLE_VALUE);
 
        if (m_hFile == INVALID_HANDLE_VALUE)
            return false;
 
        if (file_offset < 0xFFFFFFFF)
        {
            DWORD new_file_offset = SetFilePointer(m_hFile, (LONG)file_offset, 
                NULL, FILE_BEGIN);
 
            if (new_file_offset != file_offset)
                return false;
        }
 
        DWORD read;
 
        if (!ReadFile(m_hFile, buffer, bytes_to_read, &read, NULL))
            return false;
 
        if (!bytes_read)
            return read == bytes_to_read;
 
        *bytes_read = read;
 
        return true;
    }
 
    private:
    HANDLE m_hFile;
};
 
//-----------------------------------------------------------------------------

class CCheckForExportedSymbols
{
public:
    CCheckForExportedSymbols();
    ECFESResult CheckForExportedSymbols(LPCTSTR dll_path, 
        const char* symbols[], int symbol_count);
    
private:
    bool ReadHeaders(int symbol_count);
    bool CheckSymbols(const char* symbols[], int symbol_count);
    bool Error(ECFESResult res);
    bool Read(void* buffer, DWORD bytes_to_read, 
        DWORD file_offset = 0xFFFFFFFF);
        
private:
    ECFESResult m_Result;
    CCFESFile m_File;
    IMAGE_DATA_DIRECTORY m_ExportDirLocation; // RVA and size
    SRVAToFileOffset m_RVAToFileOffset;
};
 
CCheckForExportedSymbols::CCheckForExportedSymbols()
: m_Result(eCFES_OK)
{
    memset(&m_ExportDirLocation, 0, sizeof(m_ExportDirLocation));
}
 
bool CCheckForExportedSymbols::ReadHeaders(int symbol_count)
{
    IMAGE_DOS_HEADER dos_hdr;
 
    if (!Read(&dos_hdr, sizeof(dos_hdr)))
        return false;
 
    if (dos_hdr.e_magic != IMAGE_DOS_SIGNATURE)
        return Error(eCFES_InvalidDosHeader);
 
    IMAGE_NT_HEADERS64 hdr;
 
    if (!Read(&hdr, sizeof(hdr) 
        - sizeof(hdr.OptionalHeader) + sizeof(WORD), dos_hdr.e_lfanew))
        return false;
 
    if (hdr.Signature != IMAGE_NT_SIGNATURE)
        return Error(eCFES_InvalidNTHeader);
 
    if (hdr.OptionalHeader.Magic != 
        IMAGE_NT_OPTIONAL_HDR32_MAGIC && 
        hdr.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
        return Error(eCFES_InvalidNTHeader);
 
    if ((hdr.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0)
        return Error(eCFES_NotDLL);
 
    // In case of zero function names we only check for the validity of the DLL
    if (symbol_count <= 0)
        return Error(eCFES_OK);
 
    DWORD opt_hdr_size = min((DWORD)sizeof(hdr.OptionalHeader), 
        (DWORD)hdr.FileHeader.SizeOfOptionalHeader);
 
    if (hdr.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
    {
        // 32 bit binary
        if (opt_hdr_size < offsetof(IMAGE_OPTIONAL_HEADER32, 
            DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT+1]))
            return Error(eCFES_MissingFunctions);
 
        if (!Read((WORD*)&hdr.OptionalHeader.Magic + 1, 
            opt_hdr_size - sizeof(WORD)))
            return false;
 
        IMAGE_NT_HEADERS32 &hdr32 = *(IMAGE_NT_HEADERS32*) & hdr;
 
        if (hdr32.OptionalHeader.NumberOfRvaAndSizes 
            < IMAGE_DIRECTORY_ENTRY_EXPORT + 1)
            return Error(eCFES_MissingFunctions);
 
        m_ExportDirLocation = 
            hdr32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    }
    else
    {
        // 64 bit binary
        if (opt_hdr_size < offsetof(IMAGE_OPTIONAL_HEADER64, 
            DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT+1]))
            return Error(eCFES_MissingFunctions);
 
        if (!Read((WORD*)&hdr.OptionalHeader.Magic + 1, 
            opt_hdr_size - sizeof(WORD)))
            return false;
 
        if (hdr.OptionalHeader.NumberOfRvaAndSizes 
            < IMAGE_DIRECTORY_ENTRY_EXPORT + 1)
            return Error(eCFES_MissingFunctions);
 
        m_ExportDirLocation = 
            hdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    }
 
    // reading the section headers in order to be able to calculate file 
    // offsets from RVAs
    if (hdr.FileHeader.NumberOfSections)
    {
        m_RVAToFileOffset.resize(hdr.FileHeader.NumberOfSections);
        DWORD section_header_file_offset = dos_hdr.e_lfanew + sizeof(
            hdr.Signature) + sizeof(hdr.FileHeader) 
            + hdr.FileHeader.SizeOfOptionalHeader;
 
        if (!Read(&m_RVAToFileOffset[0], hdr.FileHeader.NumberOfSections 
            * sizeof(IMAGE_SECTION_HEADER), section_header_file_offset))
            return false;
    }
 
    return true;
}
 
bool CCheckForExportedSymbols::CheckSymbols(const char* symbols[], 
    int symbol_count)
{
    // finding out the length of the longest function name that we are 
    // looking for

    std::set
        <DWORD> symbol_lengths;
 
    DWORD max_funcname_len = 0;
 
    for (int i = 0; i < symbol_count; ++i)
    {
        DWORD len = (DWORD)lstrlenA(symbols[i]);
        symbol_lengths.insert(len);
        max_funcname_len = max(max_funcname_len, len);
    }
 
    // reading the export directory
    assert(m_ExportDirLocation.Size >= sizeof(IMAGE_EXPORT_DIRECTORY));
 
    IMAGE_EXPORT_DIRECTORY export_dir;
 
    if (!Read(&export_dir, sizeof(export_dir), m_RVAToFileOffset(
        m_ExportDirLocation.VirtualAddress)))
        return false;
 
    if (export_dir.NumberOfNames < (DWORD)symbol_count)
        return Error(eCFES_MissingFunctions);
 
    // Collecting the function names exported by the DLL. We omit function 
    // names whose length doesn't match
    // the length of any of the function names that we are searching for.
    std::vector<DWORD> exported_symbol_rvas;
 
    exported_symbol_rvas.resize(export_dir.NumberOfNames);
 
    if (!Read(&exported_symbol_rvas[0], sizeof(DWORD)*export_dir.NumberOfNames,
        m_RVAToFileOffset(export_dir.AddressOfNames)))
        return false;
 
    std::set
        <std::string> exported_symbols;
 
    std::vector<char> buf(max_funcname_len + 1, 0);
 
    for (std::vector<DWORD>::const_iterator it = exported_symbol_rvas.begin(),
        eit = exported_symbol_rvas.end(); it != eit; ++it)
    {
        DWORD read;
 
        if (!m_File.Read(&buf[0], max_funcname_len + 1, m_RVAToFileOffset(*it),
            &read))
            return Error(eCFES_ErrorReadingFile);
 
        DWORD buf_size = min(max_funcname_len + 1, read);
 
        DWORD len = 0;
 
        for (DWORD i = 0; i < buf_size; ++i)
        {
            if (!buf[i])
                break;
 
            ++len;
        }
 
        if (len >= buf_size)
            continue;
 
        if (!symbol_lengths.count(len))
            continue;
 
        exported_symbols.insert(&buf[0]);
    }
 
    if ((int)exported_symbols.size() < symbol_count)
        return Error(eCFES_MissingFunctions);
 
    for (int i = 0; i < symbol_count; ++i)
    {
        if (!exported_symbols.count(symbols[i]))
            return Error(eCFES_MissingFunctions);
    }
 
    return Error(eCFES_OK);
}
 
bool CCheckForExportedSymbols::Error(ECFESResult res)
{
    m_Result = res;
    return false;
}
 
bool CCheckForExportedSymbols::Read(void* buffer, DWORD bytes_to_read, 
    DWORD file_offset)
{
    DWORD read;
 
    if (!m_File.Read(buffer, bytes_to_read, file_offset, &read))
        return Error(eCFES_ErrorReadingFile);
 
    if (read != bytes_to_read)
        return Error(eCFES_DLLStructureError);
 
    return true;
}
 
ECFESResult CCheckForExportedSymbols::CheckForExportedSymbols(LPCTSTR dll_path,
    const char* symbols[], int symbol_count)
{
    if (!m_File.OpenForRead(dll_path))
        return eCFES_ErrorOpeningFile;
 
    if (!ReadHeaders(symbol_count))
        return m_Result;
 
    if (!CheckSymbols(symbols, symbol_count))
        return m_Result;
 
    return eCFES_OK;
}
 
//-----------------------------------------------------------------------------

ECFESResult CheckForExportedSymbols(LPCTSTR dll_path, const char* symbols[], 
    int symbol_count)
{
    CCheckForExportedSymbols CFES;
    return CFES.CheckForExportedSymbols(dll_path, symbols, symbol_count);
}
 
Test.cpp:
#include "stdafx.h"
#include "CheckForDllExportedSymbols.h"
#include <cstdio>
int main()
{
    static const TCHAR DLL[] = TEXT("e:\\PluginDLL.dll");
    static const char* EXPORTED_SYMBOLS[] = { "fnPluginDLL" };
 
    printf("%d\n", CheckForExportedSymbols(DLL,
        EXPORTED_SYMBOLS,
        sizeof(EXPORTED_SYMBOLS) / sizeof(EXPORTED_SYMBOLS[0])));
 
    return 0;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

pasztorpisti
Software Developer (Senior) CRYTEK
Hungary Hungary
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmembergndnet8-Aug-12 1:33 
GeneralReason for my vote of 5 Very nicely done! I using the same t... Pinmembernv313-Dec-11 3:40 
GeneralReason for my vote of 5 nice article Pinmembererewrwerq20-Dec-10 16:28 
GeneralReason for my vote of 5 new information for me and its usefu... Pinmembermutpan13-Dec-10 18:56 
General@SledgeHammer01: Actually you pointed out something that was... Pinmemberpasztorpisti6-Dec-10 22:37 
GeneralReason for my vote of 5 It will be great if this peace of co... PinmemberMember 43208446-Dec-10 19:16 
General@pasztorpisti: Fair enough. That is something I did not thin... PinmemberSledgeHammer016-Dec-10 19:02 
GeneralReason for my vote of 5 5 for taking the time to write an ar... PinmemberDRLaverdure6-Dec-10 14:50 
GeneralI used this to detect older versions of some plugins in a sy... Pinmemberpasztorpisti6-Dec-10 14:33 
GeneralReason for my vote of 2 Wouldn't LoadLibrary() and GetProces... PinmemberSledgeHammer016-Dec-10 13:38 
GeneralLoadLibraryEx Pinmemberwaleri11-Feb-11 1:31 
GeneralRe: LoadLibraryEx Pinmemberpasztorpisti11-Feb-11 8:30 
GeneralAutomatic Finder for DLL contents(methods) PinmemberMember 43208446-Dec-10 19:11 
GeneralRe: Automatic Finder for DLL contents(methods) Pinmemberpasztorpisti6-Dec-10 22:58 
GeneralRe: Automatic Finder for DLL contents(methods) PinmemberMember 43208447-Dec-10 2:49 
GeneralRe: Automatic Finder for DLL contents(methods) PinmemberJackDingler15-Mar-12 6:23 
QuestionRe: Automatic Finder for DLL contents(methods) PinmemberMember 432084415-Mar-12 9:30 
AnswerRe: Automatic Finder for DLL contents(methods) PinmemberJackDingler15-Mar-12 9:39 
GeneralThanks for the code! PinmemberT800G6-Dec-10 6:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 19 Dec 2010
Article Copyright 2010 by pasztorpisti
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid