{"id":108813,"date":"2023-09-22T07:00:00","date_gmt":"2023-09-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108813"},"modified":"2023-09-22T06:32:25","modified_gmt":"2023-09-22T13:32:25","slug":"20230922-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230922-00\/?p=108813","title":{"rendered":"When I try to call an exported function, the target crashes when it tries to call any Windows function"},"content":{"rendered":"<p>A customer reported that they had a program that loaded a DLL, then called <code>Get\u00adProc\u00adAddress<\/code> to locate an exported function. But when they called that exported function, the DLL crashed at the point it calls out to any Windows API.<\/p>\n<p>The function is exported like this:<\/p>\n<pre>extern \"C\" __declspec(dllexport) int AwesomeFunction();\r\n<\/pre>\n<p>but when it calls any Windows function, such as <code>Create\u00adMutex<\/code>, it crashes with<\/p>\n<pre style=\"white-space: pre-wrap;\">Exception thrown at 0x0000000000025A0C in CONTOSO.exe: 0xC0000005: Access violation executing location 0x0000000000025A0C.\r\n<\/pre>\n<p>That&#8217;s not a lot of information to go on, but I guessed that they were accidentally loading their DLL with <code>DONT_<wbr \/>RESOLVE_<wbr \/>DLL_<wbr \/>REFERENCES<\/code>.<\/p>\n<p>I guessed this because the invalid code address looks a lot like a relative virtual address. Relative virtual addresses have a maximum value of the virtual size of the mapped image. If you assume that most DLLs have under 10MB of combined code and data, this puts a cap of <code>0x00a00000<\/code> on the relative virtual address.<\/p>\n<p>For DLLs that are not bound, the entry in the import address table is the relative virtual address of the name of the function being imported. So it looks like the code is jumping not to the target function but to the relative virtual address that holds the name of the function it&#8217;s trying to call. Which means that the imported function was never resolved. And that happens if you pass <code>DONT_<wbr \/>RESOLVE_<wbr \/>DLL_<wbr \/>REFERENCES<\/code>.<\/p>\n<p>The customer insisted that they weren&#8217;t doing that, and they shared some code to prove it.\u00b9<\/p>\n<pre>\/\/ Litware, the exporting DLL\r\n\r\nextern \"C\" __declspec(dllexport) int GetLitwareVersion()\r\n{\r\n    return 42;\r\n}\r\n\r\nextern \"C\" __declspec(dllexport) int AwesomeFunction()\r\n{\r\n    \/\/ If we delete this line, it just crashes at the next\r\n    \/\/ place we call a Windows function.\r\n    auto mutex = CreateMutexW(nullptr, FALSE, nullptr);\r\n    if (mutex == nullptr) {\r\n        return -1;\r\n    }\r\n\r\n    \/\/ ... etc ...\r\n\r\n    CloseHandle(mutex);\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<p>And the consumer looks like this:<\/p>\n<pre>\/\/ Contoso, the consuming executable\r\n\r\n#include &lt;windows.h&gt;\r\n\r\nint main(int argc, char** argv)\r\n{\r\n    auto dll = LoadLibraryW(L\"litware.exe\");\r\n    if (!dll) return 1;\r\n\r\n    auto getLitwareVersion = ((int(*)())\r\n        GetProcAddress(dll, \"GetLitwareVersion\");\r\n    if (!getLitwareVersion) return 1;\r\n\r\n    \/\/ This works because GetLitwareVersion doesn't\r\n    \/\/ call any Windows functions.\r\n    auto version = getLitwareVersion();\r\n\r\n    auto awesomeFunction = ((int(*)())\r\n        GetProcAddress(dll, \"AwesomeFunction\");\r\n    if (!awesomeFunction) return 1;\r\n\r\n    \/\/ This crashes once AwesomeFunction tries to call\r\n    \/\/ a Windows function.\r\n    auto result = awesomeFunction();\r\n\r\n    return result;\r\n}\r\n<\/pre>\n<p>Did you spot the problem?<\/p>\n<p>The customer said they were loading a DLL, but in their code, they are loading <code>L\"litware.exe\"<\/code>. When asked about this, they said, &#8220;But it doesn&#8217;t matter. Executables can export functions too.&#8221;<\/p>\n<p>While it&#8217;s true that executables can export functions, it&#8217;s not true that you can load an executable as a DLL. The documentation notes that<\/p>\n<blockquote class=\"q\"><p>If the specified module is an executable module, static imports are not loaded; instead, the module is loaded as if by <code>LoadLibraryEx<\/code> with the <code>DONT_<wbr \/>RESOLVE_<wbr \/>DLL_<wbr \/>REFERENCES<\/code> flag.<\/p><\/blockquote>\n<p>So they were in fact loading the DLL with the <code>DONT_<wbr \/>RESOLVE_<wbr \/>DLL_<wbr \/>REFERENCES<\/code> flag; they just didn&#8217;t realize it.<\/p>\n<p>One lesson from this is &#8220;Don&#8217;t load executables as if they were DLLs.&#8221; But really, the purpose of this story is to show how you can diagnose a problem by looking at the evidence and combining it with what you know to come up with plausible theories as to what went wrong. Even though my theory was, strictly speaking, wrong, it steered us in the right direction.<\/p>\n<p>\u00b9 This ended up being another one of those weird psychological tricks: To get the customer to share their code, you have to accuse them of doing something silly, and that goads them into sharing the code to prove you wrong. I&#8217;m willing to suffer the disgrace of being proven wrong if it means you&#8217;ll share the malfunctioning code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using what you know about how functions are imported to construct a theory that matches the evidence.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108813","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Using what you know about how functions are imported to construct a theory that matches the evidence.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108813","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108813"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108813\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108813"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108813"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108813"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}