Thread: Who Called My DLL? And Component Security.

  1. #1
    Registered User
    Join Date
    Feb 2003
    Posts
    265

    Who Called My DLL? And Component Security.

    OK, I have a library, mylib.dll. It can be loaded from various applications. What i would like to know is when it is loaded, and some function is called, how do i find the path to the executable that just loaded it?

    Consequencially, I am also wondering if there is a good way to go about making sure that the code accessing my library is in fact code i have writen. Right now I am using a MD5 hash of the calling executable, and comparing it to an approved list in the DLL, however this approach is far from perfect, and quite labor intensive. How do i go about assuring communication is between trusted components and not third party immitations?

  2. #2
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    link it as a static lib and you will not have such a problem... Nowdays there is no disk space problem, so why do you want to use dll if there is such a requirement of security?
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  3. #3
    Registered User
    Join Date
    Feb 2003
    Posts
    265
    The applications referencing it arent writen in C++.

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> OK, I have a library, mylib.dll. It can be loaded from various applications. What i would like to know is when it is loaded, and some function is called, how do i find the path to the executable that just loaded it? <<

    I think you can do this by calling GetModuleHandle with NULL and then using the returned handle in a call to GetModuleFileName.

    >> Consequencially, I am also wondering if there is a good way to go about making sure that the code accessing my library is in fact code i have writen. Right now I am using a MD5 hash of the calling executable, and comparing it to an approved list in the DLL, however this approach is far from perfect, and quite labor intensive. How do i go about assuring communication is between trusted components and not third party immitations? <<

    Once you have distributed code, you have lost control of it. However, if you don't document your DLL, use some basic obfuscation (in terms of exported function names and arguments), and depending on what the DLL does, it may be that no-one will go to the effort of working out how to call it.

  5. #5
    Registered User
    Join Date
    Feb 2003
    Posts
    265

    thankyou

    Im using the following code and it seems to work great.

    Code:
    std::string PathToOurFile = "";
    LPTSTR  TempPathToOurFile = new TCHAR[_MAX_PATH];
    GetModuleFileName(GetModuleHandle(NULL),TempPathToOurFile,_MAX_PATH);
    for(int x=0;TempPathToOurFile[x]!=NULL;x++) { PathToOurFile += (char)TempPathToOurFile[x]; }
    while(PathToOurFile[PathToOurFile.length()-1]==' ') { PathToOurFile = PathToOurFile.substr(0,PathToOurFile.length()-2); }
    Just to make sure, please let me know if anybody sees a problem with it. Im not really a winapi programmer.

    Thanks.

  6. #6
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Code:
    //for(int x=0;TempPathToOurFile[x]!=NULL;x++) { PathToOurFile += (char)TempPathToOurFile[x]; }
    PathToOutFile = TempPathToOurFile; //can assign const char* to std::string
    Code:
    while(PathToOurFile[PathToOurFile.length()-1]==' ') { PathToOurFile = PathToOurFile.substr(0,PathToOurFile.length()-2); }
    Is this trimming the end of the string of spaces? In which case you could a) first loop to find first non-space and use a single substr call, or b) use rfind_first_not_of() method instead of the loop.

    (By the way, why do you think there would be spaces at the end of the string. If GetModuleFileName is shorter than MAX_PATH - does it have a leading underscore? - then surely it won't fill the rest of the array with spaces.)

    As to the whole problem, are you sure you need all that protection? Does your DLL really offer something that would make it worth stealing and is not provided by free libraries?

    One problem could also be that the Windows file system is not case-sensitive. At least this remark seems to indicate this:
    Remarks

    If a module is loaded in two processes, its module filename in one process may differ in case from its module filename in the other process.
    It is entirely possible that the DLL won't recognize your program because the case is different. And what if the user decides to rename the calling exe?

    May-be the DLL functions could take a "password" as an additional parameter ?
    Last edited by anon; 01-06-2007 at 06:25 PM.

  7. #7
    Registered User
    Join Date
    Feb 2003
    Posts
    265
    PathToOutFile = TempPathToOurFile; //can assign const char* to std::string
    "TempPathToOurFile" is not a const char *, its a wide character array, which cant be directly assigned to a string. Thus each character has to be cast individually and concatinated.

    The real answer to this situation is some kind of code signing certificate service, which im sure can be purchased from microsoft or another vendor at an exorbinant rate. I am aiming for the best security, based on file signatures, without spending the entire budget to pay for protection.

    "May-be the DLL functions could take a "password" as an additional parameter?"
    Unfortunately, the GUI is provided by C# applications, which are for the most part a reversable language. Any password would be easy to duplicate and fool. My current implimentation actually uses the filename, and produces a MD5 hash of the calling executable. The valid md5 hashes are hard coded and base64'd inside the DLL, so it wont be easy to find and hex edit.

    I have explored several security options, and I feel that the calls provided in the dll (interaction with a secure network service. The authentication mechanism and message encoding for the server are security through obscurity, and the protocol is very difficult to reverse engineer without knowing the datastructures used internally etc) are worth the added security. I know beyond a shadow of a doubt that there is no such thing as uncrackable code, however the skill level of the attacker required to subvert the system will be greatly increased, and the server side will most deffinetly notice any protocol breaches (from an attacker attempting to reverse engineer the protocol) and report it.

    "And what if the user decides to rename the calling exe?"
    The code provided will return the full path (including the filename) of the calling executable file.

    "Is this trimming the end of the string of spaces? In which case you could a) first loop to find first non-space and use a single substr call, or b) use rfind_first_not_of() method instead of the loop. (By the way, why do you think there would be spaces at the end of the string. If GetModuleFileName is shorter than MAX_PATH - does it have a leading underscore? - then surely it won't fill the rest of the array with spaces.)"
    Spaces could occour in a valid path: "C:\Documents and Settings\username\whatever\asdf.exe". If i only searched for the first space, i would only get "C:\Documents". Unfortunately there is the potential that one or more spaces are at the end, they are placed there by the getmodulefilename function, not my code in an array initialization. You are right that i could optimize it, but ill pay a few extra cycles for such a simplistic easy to follow and problem free operation =P

    I really appreciate the replies and ideas. It always helps to have somebody to bounce your plan off of. I have been writing small (mostly useless) test applications to duplicate the functionality used in the application to simplify the environment and perform various experiments.
    Last edited by Geolingo; 01-06-2007 at 08:02 PM.

  8. #8
    The larch
    Join Date
    May 2006
    Posts
    3,573
    "TempPathToOurFile" is not a const char *, its a wide character array, which cant be directly assigned to a string. Thus each character has to be cast individually and concatinated.
    Ok, so you are using Unicode? The reference says that TCHAR is Unicode character or Windows character, and LPTSTR is a "pointer to a null-terminated Windows or Unicode character string." (probably depending on whether UNICODE has been defined by you). If "Windows character" is a normal 1-byte ASCII character, you could just use a char* buffer.

    Code:
    #include <windows.h>
    #include <iostream>
    int main()
    {
        char name[MAX_PATH];
        GetModuleFileName(GetModuleHandle(NULL), name, MAX_PATH);
        std::cout << name << std::endl;
        std::cin.get();
    }
    Spaces could occour in a valid path: "C:\Documents and Settings\username\whatever\asdf.exe". If i only searched for the first space, i would only get "C:\Documents".
    I suggested finding the first non-space backwards: rfind_first_not_of(' ')

    Unfortunately there is the potential that one or more spaces are at the end, they are placed there by the getmodulefilename function, not my code in an array initialization.
    Really? File names can't end with spaces, so if GetModuleFileName put extra spaces there, that would be a serious bug in WinAPI. (May-be that is related to using UNICODE?) Anyway, GetModuleFileName returns the number of characters written to the buffer.

    Anyway, if you hash the full filename, you also have the problem that all users must have your program in a certain directory. What if they don't want to install your program to "My Documents", or have a localized version of Windows where it is called something like "Mein Dokumenten"? To make this approach work, you should find the last backslash and ignore everything before it.

    And you should also convert the executable name into lower (or upper) case, because you might get all kinds mixtures of cases.

  9. #9
    Registered User
    Join Date
    Feb 2003
    Posts
    265
    My code never specifys unicode, However:
    Code:
    char TempPathToOurFile[_MAX_PATH];
    GetModuleFileName(GetModuleHandle(NULL),TempPathToOurFile,_MAX_PATH);
    Results in:
    Code:
    error C2664: 'GetModuleFileNameW' : cannot convert parameter 2 from 'char [260]' to 'LPWCH'
    The function GetModuleFileName seems to map automaticly to a wide variant (GetModuleFileNameW). I never perform any preprocessor definitions specifying unicode, and would love to know where such a thing might reside because i hate having to convert.

    Anyway, if you hash the full filename...
    I am not hashing the fileNAME. I'm hashing the actual FILE at the location returned by GetModuleFileName. Thus it doesnt matter where the file is or what its named, so long as its contents havent changed since the DLL was compiled.

    Where exactly did you get rfind_first_not_of? Google returns exactly 2 hits: http://www.google.com/search?hl=en&l...d_first_not_of
    Last edited by Geolingo; 01-07-2007 at 10:50 AM.

  10. #10
    The larch
    Join Date
    May 2006
    Posts
    3,573
    If you get an error complaining about GetModuleFileNameW - which is a wide char version of the function, try GetModuleFileNameA. Some reading shows that Windows API names are actually macros that are either translated to -W or -A versions, depending on whether UNICODE is defined. http://www.jorendorff.com/articles/unicode/windows.html

    You could also look in your project settings to see if UNICODE is defined automatically, or may-be use #undef UNICODE.

    rfind is a std::string method which is entirely similar to simple find, except that it works backwards, starting at the end of the string. It has all the family of string find methods normal find has.

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by DeepBlackMagic
    "TempPathToOurFile" is not a const char *, its a wide character array, which cant be directly assigned to a string. Thus each character has to be cast individually and concatinated.
    NOOOOOOOOOO!

    For heaven's sake, if you don't understand Unicode, learn it instead of randomly assigning something and assuming it works just because it happens to work in your particular (limited) case.
    You can't just cast WCHAR to char. The moment you have a character outside the ASCII range, it will produce bogus results.

    You can use a std::basic_string<TCHAR> instead of std::string - it will simply work. (But don't forget to use the TEXT() macro for all string and character literals.)

    Unfortunately there is the potential that one or more spaces are at the end, they are placed there by the getmodulefilename function
    Where do you get this idea? Unless there is a horrible bug in GetModuleFileName which has never been found (very unlikely), it most definitely won't do any such thing. If there are spaces at the end of the string, then only because they're part of the file name (which is legal AFAIK, if uncommon).
    I agree with anon: the real source of potential spaces, if there are any, can only be your broken Unicode->ANSI conversion.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by anon
    If you get an error complaining about GetModuleFileNameW - which is a wide char version of the function, try GetModuleFileNameA. Some reading shows that Windows API names are actually macros that are either translated to -W or -A versions, depending on whether UNICODE is defined.
    I wouldn't do it. If macro is exctracted to the UNICODE version - it means the UNICODE or _UNICODE is somewhere defined.

    Better yet to figure out why and where and remove it in a proper way
    You could also look in your project settings to see if UNICODE is defined automatically, or may-be use #undef UNICODE.
    when to mess with the random calling of the unucode and not-unicode functions in the same project
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    VS.Net 2005 enables the UNICODE and _UNICODE macros by default in the project settings.

    But it's still a good idea to accommodate this setting instead of disabling it. Make your programs Unicode-ready. They'll be faster (WinXP and friends internally convert non-Unicode strings from and to Unicode before and after every API call) and "internationaler"
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. OCR DLL component
    By USBFreak in forum C++ Programming
    Replies: 2
    Last Post: 07-26-2008, 08:30 PM
  2. exe work but dll does not work?
    By George2 in forum C++ Programming
    Replies: 1
    Last Post: 08-06-2007, 02:18 AM
  3. MSI With Shared Component Question
    By mercury529 in forum Windows Programming
    Replies: 0
    Last Post: 11-18-2006, 07:36 PM
  4. COM dll isolation
    By Unregistered in forum C++ Programming
    Replies: 0
    Last Post: 07-09-2002, 04:58 PM