Skip to content

Crash in MemoryLoadLibraryEx(): 0xC0000005: Access violation reading location 0x00000000. #31

Open

Description

@Gama11

I'm using the latest version of MemoryModule (f02a8e6).

To reproduce, replace SampleDLL.cpp with:

class Callable
{
public:
    virtual int call() { return 0; }
};

Callable * GetCallable()
{
    static Callable callable;
    return &callable;
}

int i = GetCallable()->call();

(this should be valid C++ code as far as I'm aware)

and DllLoader.cpp with:

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <malloc.h>

#include "../../MemoryModule.h"

#define DLL_FILE TEXT("..\\SampleDLL\\SampleDLL.dll")

void LoadFromFile(void)
{
    HINSTANCE handle = LoadLibrary(DLL_FILE);
    if (handle == NULL)
        return;

    FreeLibrary(handle);
}

void LoadFromMemory(void)
{
    FILE *fp;
    unsigned char *data=NULL;
    size_t size;
    HMEMORYMODULE handle;

    fp = _tfopen(DLL_FILE, _T("rb"));
    if (fp == NULL)
    {
        _tprintf(_T("Can't open DLL file \"%s\"."), DLL_FILE);
        goto exit;
    }

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    data = (unsigned char *)malloc(size);
    fseek(fp, 0, SEEK_SET);
    fread(data, 1, size, fp);
    fclose(fp);

    handle = MemoryLoadLibrary(data);
    if (handle == NULL)
    {
        _tprintf(_T("Can't load library from memory.\n"));
        goto exit;
    }

    MemoryFreeLibrary(handle);

exit:
    if (data)
        free(data);
}

int main(int argc, char* argv[])
{
    //LoadFromFile();
    LoadFromMemory();
    return 0;
}

If LoadFromFile() is run, the program exits with 0.

However, if LoadFromMemory() is run, it crashes with:

Exception thrown at 0x0008146E in DllLoader.exe: 0xC0000005: Access violation reading location 0x00000000.

Stacktrace:

    0008146e()  Unknown
    [Frames below may be incorrect and/or missing]  
    ucrtbased.dll!__initterm�() Unknown
    00082d41()  Unknown
    00082be9()  Unknown
    00082fbd()  Unknown
    000831df()  Unknown
>   DllLoader.exe!MemoryLoadLibraryEx(const void * data, void * (const char *, void *) * loadLibrary, int (...) * (void *, const char *, void *) * getProcAddress, void (void *, void *) * freeLibrary, void * userdata) Line 560   C
    DllLoader.exe!MemoryLoadLibrary(const void * data) Line 433 C
    DllLoader.exe!LoadFromMemory() Line 42  C++
    DllLoader.exe!main(int argc, char * * argv) Line 60 C++
    DllLoader.exe!invoke_main() Line 74 C++
    DllLoader.exe!__scrt_common_main_seh() Line 264 C++
    DllLoader.exe!__scrt_common_main() Line 309 C++
    DllLoader.exe!mainCRTStartup() Line 17  C++
    kernel32.dll!@BaseThreadInitThunk@12�() Unknown
    ntdll.dll!___RtlUserThreadStart@8�()    Unknown
    ntdll.dll!__RtlUserThreadStart@8�() Unknown

Line 560 in MemoryModule.c:

// notify library about attaching to process
BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);

Strangely enough, if both LoadFromLibrary() and LoadFromMemory() are run (and in that order!), there is no crash.

Activity

  1. dantje commented on Nov 29, 2015

    @dantje

    The VS2015 generates a TLS (thread local storage) section for the SampleDLL, so this
    https://support.microsoft.com/en-us/kb/118816 could be related.

    But this does not explain why it works when the DLL is loaded from a file with LoadLibrary(). The behaviour should be identical, but isn't.

    From what I can see there are no TLS callbacks in the DLL, so #25 does not fit.

    In http://www.nynaeve.net/?p=186 there is the section

    At thread initialization time, allocate and initialize TLS memory blocks for each module
    utilizing TLS, allocate the ThreadLocalStoragePointer array for the current thread, and
    link the TLS memory blocks in to the ThreadLocalStoragePointer array. Additionally,
    TLS initializers and then DLL initializers (in that order) are invoked for the current thread."

    I don't see where MemoryModule is doing this.

  2. own2k commented on Nov 29, 2015

    @own2k

    Great. How easy would be to protect code from MemoryModule.

  3. dantje commented on Nov 29, 2015

    @dantje

    Upon further reading the wonderful article series by Ken Johnson, I think that MemoryModule currently behaves just like pre-Vista Windows did with regards to implicit TLS in DLLs.

    MemoryLoadLibraryEx() does not allocate the necessary structures, but compiler and linker generated a DLL that expect these to be in place. Mayhem ensues.

    http://www.nynaeve.net/?p=187 describes the situation in a slightly discouraging fashion:

    This results in one of the absolute worst possible kinds of problems to debug. Now you’ve got one module trampling all over another module’s state, with the guilty module under the (mistaken) belief that the state that it’s referencing is really the guilty module’s own state. If you’re lucky, the process has no implicit TLS using at all (at process initialization time), and the ThreadLocalStoragePointer will not be allocated for the current thread and the initial access to a declspec(thread) variable will simply result in an __immediate null pointer dereference

    Emphasis mine. I guess this initial access happens in GetCallable() when the control flow reaches the static variable for the first time.

    And because Windows after Vista gained support for implicit TLS in DLLs (http://www.nynaeve.net/?p=189), the regular LoadLibrary() works fine. And if it happens to be called before MemoryLoadLibraryEx() it will (IMHO) allocate everything for the process to access the TLS variables without a glitch.

  4. dantje commented on Nov 29, 2015

    @dantje

    Not really MemoryModule related, but one question is why the compiler generates TLS accesses without any occurrence of "declspec(thread)" in the source. This is because of magic statics which are a new feature in VS2015 (https://www.visualstudio.com/en-us/news/vs2015-vs.aspx):

    Thread-Safe "Magic" Statics Static local variables are now initialized in a thread-safe way, eliminating the need for manual synchronization. Only initialization is thread-safe, use of static local variables by multiple threads must still be manually synchronized. The thread-safe statics feature can be disabled by using the /Zc:threadSafeInit- flag to avoid taking a dependency on the CRT.

    And if this bites MemoryModule, it also affects VS2015 code running on pre-Vista Windows:

    https://connect.microsoft.com/VisualStudio/feedback/details/1715018/dll-usage-of-thread-safe-magic-statics-may-crash-on-windows-xp

  5. fancycode commented on Nov 30, 2015

    @fancycode
    Owner

    Does the DLL work in MemoryModule if compiled with the switch /Zc:threadSafeInit- as described in the link?

  6. Gama11 commented on Nov 30, 2015

    @Gama11
    Author

    Yes. In my case that's good enough. In other cases where you want to embed some existing dll that's not really helpful though since you can't control how it was compiled.

  7. dantje commented on Aug 31, 2016

    @dantje

    A recent article in the D language blog reminded me of this issue (https://dlang.org/blog/2016/08/12/project-highlight-visual-d/):

    Visual Studio is a Win32 application and loads its extensions dynamically as DLLs. D very much relies on Thread Local Storage (TLS) as it is the default for global variables. Under Windows this uses “implicit TLS” built into the binary. Unfortunately, Windows Vista was the first version to support this for dynamically loaded DLLs, but Windows XP was still the most widely used version. It only supports TLS for the DLLs that are loaded implicitly with the application.

    Eventually, after a lot of debugging, he managed to work around his Windows XP problems by tricking the system into believing a manually loaded DLL had been implicitly loaded with the application. The result of these efforts can be seen in the DRuntime modules core.sys.windows.dll and core.sys.windows.threadaux. This implementation comes with the drawback that DLLs using it cannot be unloaded. An improved version by Denis Shelomovskii works around this. Given the decline of XP usage, the need for this will eventually fade away.

    When those D DLLs are loaded/attached they set up the implicit TLS for their global variables themselves. So even under XP where the Windows loader did not handle implicit TLS they magically work.

    And the code for this is in dll.d [1] -- thus it might be a guide on how to do this in MemoryModule.

    [1] which even quotes the page from http://www.nynaeve.net mentioned above, I should have googled for that back last year, it would have been the second entry.

  8. vyrus001 commented on Nov 15, 2016

    @vyrus001

    currently running into a problem with this, as I can not use a mingw compiled binary to "load" (with memorymodule) a mingw compiled binary (the TLSCallback points to a giant (wrong) address upon the first iteration of the while loop inside "executeTLS()". Has there been any progress on fixing the implementation here (to work on modern systems) or should I simply look into trying to figure how to disable TLS support in mingw?

  9. dantje commented on Nov 15, 2016

    @dantje

    I'm not aware of any progress on this issue. If you do not rely on thread safe initialization of local statics (e.g. you do not use threads at all) you could experiment with -fno-threadsafe-statics in gcc/mingw, this is what solved our problem with VS2015.

  10. vyrus001 commented on Nov 16, 2016

    @vyrus001

    sadly, my targets are complicated (a mixture of multiple languages and a convoluted build system), and while this might work for some of them, the best solution for me would be to actually "fix" memory module to allocate things correctly (sadly this is a bit beyond me at the moment).

  11. harrysummer commented on Apr 7, 2018

    @harrysummer

    Would it be possible that some experts here could implement the required change described by http://www.nynaeve.net/?p=189? The blogger has posted the reference implementation along with the blog, although it is somewhat beyond my ability to follow that...

  12. harrysummer commented on May 25, 2018

    @harrysummer

    Hmmm. This interesting virus implementation seems to be able to solve the issue here: https://github.com/devilogic/xvirus/blob/master/EvilKernel/RefixTlsPrivate.c

    Update: did more investigation. The code is not thread-safe and does not consider special cases where acceleration is applicable. Nevertheless, I will try to use it as a starting point and see how far I can go.

    Update 2: suddenly found that WINE should have the best open-source PE loader implementation. I guess I would implement my loader directly by migrating their code.

    Update 3: the WINE implementation is based on POSIX API, might need a huge refractor to make it collaborate with Windows native process management and file management. Working on it. P.S. the virus code is poorly designed and it could have many problems related with TLS handling, e.g. only currently thread has TLS storage allocated...

  13. trungnt2910 commented on Oct 18, 2021

    @trungnt2910

    Why can't we just parse the whole binary, replace references to (void**)NtCurrentTeb()[11] in the assembly with a function call?

    In x86, TLS block array is accessed by something like:

                               .text:00406eb8 64 8b 0d 2c 00 00 00             mov    %fs:0x2c,%ecx
    

    Why can't we just replace that with a function call FF 15 DE AD BE EF, to a function at 0xefbeadde that stores the relevant address to the correct position? This is somewhat similar to the way Apple's dyld handles its TLS.

    Most workarounds for Windows seem to "reserve" a specific range of module IDs instead of legitimately acquire a real ID, allowing chaos to happen when enough modules are loaded the right way.

  14. jrmuizel commented on May 24, 2022

    @jrmuizel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions