2

Issue

Is it true that when an application is linked with an .exe (or .dll that depends on an .exe), this application will crash on windows 10 ?

Long story short

I ask this question because few days ago I had an application that had some unexpected crash on windows 10. After some investigations RbMm gives me some good explanations and reproductible examples Call of statically link function crash everytimes on windows 8/10 but not 7. It allows me to solve my issue but I couldn't believe there could be a so huge important breaking change and I couldn't find any article on this subject.

Details

The conclusion that RbMm found was :

however the root of crash on windows 10 - because windows 10 not resolve PE ("exe") import if this pe have no flag IMAGE_FILE_DLL. in other words it process this PE like LoadLibraryEx with flag DONT_RESOLVE_DLL_REFERENCES - does not load additional executable modules that are referenced by the specified module and nor resolve imports. as result this PE not initialized and will crash at first import function call (in your case this is strcmp).

Examples

And there is a simple way to reproduce the issue :

#include <windows.h>
#include <Netsh.h>
#pragma comment(lib, "Netsh.lib")
void main(int argc, char* argv[])
{
    MatchToken(L"*", L"*");// crash here on win 10
}

I also have created a simple project in order to do the trick. It build a .exe with an exported function. And another .exe that use this exported function.

Conclusion

So should I conclude that many projects are now unstable on windows 10 or did I miss some points ? (I'm sure, I did).

Edit

  • I never say it is a good practice, I am just using programs that already do this, including some Microsoft libraries (Netsh, wshelper, ...) and some useful projects (postgresql).

You can fall in this case with common call to a library like :

if (HMODULE hmod = LoadLibraryW(L"wshelper.dll"))
{
    DWORD (WINAPI * InitHelperDll)(_In_ DWORD dwNetshVersion, PVOID pReserved);

    if (*(void**)&InitHelperDll = GetProcAddress(hmod, "InitHelperDll"))
    {
        InitHelperDll(1, 0);// crash here, internally InitHelperDll call the RegisterHelper function from Netsh.exe.
//but again, because Netsh.exe not initialized (import not resolved) when it loads as DLL in win 10
    }
    FreeLibrary(hmod);
}
  • On windows 7, When debugging the functions from the linked .exe are working fine. So I guess the IAT is initialized. It also use strcmp, so the CRT is initialized.

Is this behaviour against specification ? unspecified ? or just luck ? Should I open a ticket to microsoft helpdesk ? Should I open a ticket to each project that use this behavior. Or is it a proper way to programmatically initialize IAT and CRT on windows 10 (without any modification to the .exe that export the functions) ?

Community
  • 1
  • 1
Edeen
  • 229
  • 2
  • 10
  • 3
    IMO, very few projects will be affected, because deliberately using an executable as if it were a DLL is a very strange thing to do. This has never been supported. – Harry Johnston Aug 30 '17 at 10:05
  • 1
    Hmmm. This sounds like a "if I hit my finger with a hammer, it hurts" problem. What is the reason why anyone would build his DLLs as an EXE file instead of DLL file? – Gerhardh Aug 30 '17 at 10:12
  • 1
    Agree with ^^. The conclusion "many projects are now unstable" has an unspoken assumption that many projects use an EXE as if it's a DLL. I don't believe that at all. And that's confirmed by the lack of reports of programs mysteriously crashing on Windows 10. – MSalters Aug 30 '17 at 10:21
  • assume you dynamic load *EXE* and system resolve it import. but how about call some entry point, for give it opportunity initialize itself ? this impossible. no such entry point in *EXE*. as result even with resolved import *EXE* uninit. you call some *func* from it. what be if this *func* used some data, normally initialized in *EXE* entry point ? say *EXE* use static linked *CRT* and *func* call *malloc* ? *malloc* try allocate from *CRT* heap, but heap not created yet. – RbMm Aug 30 '17 at 10:26
  • 2
    and if you want, you can easy convert private *EXE* to *DLL* - need 1. add flag `IMAGE_FILE_DLL` to `IMAGE_FILE_HEADER.Characteristics` 2. set `AddressOfEntryPoint` to 0 in `IMAGE_OPTIONAL_HEADER`. you can do this is some binary editor, or better write small own program for this modification. anyway after this is exist question of relocation - if no relocation in *PE* - you can not create it yourself. you can only hope that it default address range will be free when you load this *PE*. if you convert to *DLL* but this way - you load your *postrgres.exe* ok (import will be resolved). – RbMm Aug 30 '17 at 11:43
  • but still possible fail or crash on some exported function call, because native image entry point is not called – RbMm Aug 30 '17 at 11:43
  • The modification of the PE with this flag works fine. But I have to keep the binaries of other installed application (.exe that export functions) without any modification. – Edeen Aug 30 '17 at 14:47
  • Hmm, I don't think that is the correct story. Making an executable file dynamically loadable first of all requires it to be *relocatable*. So it can be loaded at an arbitrary address, whatever happens to be available. An EXE is traditionally built *without* relocation info. None is needed because an EXE can always be loaded at its preferred base address, it gets loaded first. But that has been changing, the /dynamicbase linker option is important today, ASLR is a very basic malware counter-measure. But whatever, the boilerplate "if it hurts then don't do it" advice applies. – Hans Passant Aug 30 '17 at 15:19
  • 1
    about *wshelper.dll* - you can look it *IAT* and view that import from *netsh.exe*. so `LoadLibraryW(L"wshelper.dll")` this is indirect load *netsh.exe*. begin from win8.1 loader not resolve *exe* import, as result and crash when *wshelper.dll* make call to *netsh.RegisterHelper* on windows 7 - import of exe resolved - so no crash. but this show that *wshelper.dll* design only be loaded to *netsh.exe* process – RbMm Aug 30 '17 at 21:44
  • If you can find documentation anywhere on MSDN that tells you that you are allowed to load `wshelper.dll` into your own process, then the fact that it doesn't work any more is a bug. I suspect there is no such documentation, you're just misusing an internal DLL. – Harry Johnston Aug 31 '17 at 03:56
  • Also, can you point to PostgreSQL documentation that tells you to link to `postgres.exe` as if it were a library? I suspect you were never supposed to be doing that either. – Harry Johnston Aug 31 '17 at 03:57
  • @Harry Johnston, I'm not using postgres.exe as library but some extensions with command line interface do it. So if I understand well. There is a 'regression' on windows 10, but it is to promote good practice, and all the concerned projects have to be fixed. – Edeen Aug 31 '17 at 07:26
  • I'm not sure I'd call it a regression, but whatever. Yes, if someone's project includes an executable that crashes on Windows 10 because it is linking to another executable, that's a bug in the project. – Harry Johnston Aug 31 '17 at 08:52
  • @HarryJohnston: So using a well-known, publicly-documented (not from `winternl.h`, etc.) function from the Windows SDK is a bug in the project? That's completely unreasonable. Or perhaps the bug is even linking with a standard LIB from the Windows SDK? Who would've thought linking with the LIBs supplied in the Windows SDK is safe or supported? – conio Oct 01 '17 at 18:02
  • @conio, [so far as I can see,](https://msdn.microsoft.com/en-us/library/windows/desktop/ms708327(v=vs.85).aspx) InitHelperDll is only documented as a callback function - it is a function for the Win32 programmer to write, not part of the SDK for the Win32 programmer to call. I'm also unaware of any LIB file containing it. If you know otherwise, I'm all ears. – Harry Johnston Oct 01 '17 at 20:09
  • ... ah, you're probably talking about MatchToken, not InitHelperDll. Yes, looking at the documentation, I can see why someone might be misled. I can only assume whoever wrote that [took it for granted](http://lesswrong.com/lw/kg/expecting_short_inferential_distances/) that the reader would already be familiar with basic Windows principles. That's still carelessness on Microsoft's part. Good point. @conio – Harry Johnston Oct 01 '17 at 20:16
  • @HarryJohnston: I'm not sure I understand. You say the documentation took a misleading shortcut as if there's another way someone who's familiar with basic Windows principles would realize. What's the other way? The function - as documented - is only available through the `netsh.exe` "DLL" or the `netsh.lib` library (which is an import lib rather than a lib with full implementation). I don't see the alternative. The documentation says as explicitly as can ebe xpected from MSDN to dynamically link with `netsh.exe`. Either it is wrong, and not just misleading, or linking to an EXE is not a bug. – conio Oct 01 '17 at 20:29
  • @conio, you're only supposed to be using those functions from a NetShell Helper, i.e., a DLL that `netsh.exe` has loaded. It is a little unusual for a DLL to link back to the executable that loaded it, but it is fully supported. I'm adding a "community note" to the relevant documentation now. – Harry Johnston Oct 01 '17 at 20:32
  • 1
    @HarryJohnston: Fair enough. But you probably mean de-facto supported. I'm not aware an official document that says that importing from an EXE is unsupported, *unless* it's the "main program" (though, obviously, for technical but not-really-contractual reasons this is guaranteed to work; and maybe such a document exists, I don't know). Instead of these tricks there's another common Windows pattern which is to pass to an extension's initialization function a bunch of function pointers to said services. That's how `FltRegisterFilter` and `SpLsaModeInitialize` work, for example. No special rules. – conio Oct 01 '17 at 20:44
  • @conio, looks like you're right, I can't find anything official, not that that's unusual. :-) [Mutual Imports](https://msdn.microsoft.com/en-us/library/fdy23fx6.aspx) *sort of* implies that an EXE and a DLL can have mutual imports, but it's nowhere near as clear as I'd prefer. – Harry Johnston Oct 01 '17 at 21:20

0 Answers0