{"id":105800,"date":"2021-10-14T07:00:00","date_gmt":"2021-10-14T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105800"},"modified":"2021-10-14T06:59:41","modified_gmt":"2021-10-14T13:59:41","slug":"20211014-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211014-00\/?p=105800","title":{"rendered":"Why does GetModuleInfo fail to produce an entry point for executables?"},"content":{"rendered":"<p>A customer wanted to find the entry point for some executables. They tried this two-step plan:<\/p>\n<ul>\n<li>Load the executable via <code>LoadLibrary<\/code>. Like, a real <code>LoadLibrary<\/code>, none of this &#8220;as datafile&#8221; stuff.<\/li>\n<li>Call <code>Get\u00adModule\u00adInformation<\/code> and look at the <code>MODULE\u00adINFO.Entry\u00adPoint<\/code><\/li>\n<\/ul>\n<p>The customer found that the result is always <code>NULL<\/code>. What&#8217;s going on?<\/p>\n<p>First of all, it&#8217;s not clear what the purpose of the exercise is. You can&#8217;t call the entry point, since it&#8217;s not the entry point for <i>this<\/i> process. And the entry point obtained from the current process may not match the entry point when the process is actually run as a process.<\/p>\n<p>Loading an executable as a library is a pretty dodgy operation. The module can be used to load resources, but you can&#8217;t do much else with it. And if all you are after are the resources, you may as well just load it as a datafile.<\/p>\n<p>What you can do is manually walk the Portable Executable header (either by seeking and reading or by using a memory-mapped file) to get the <code>Address\u00adOf\u00adEntry\u00adPoint<\/code>, which is a relative virtual address. You can then add that to the base address of the module (when it eventually loads) to locate the entry point.<\/p>\n<p>Okay, fine. But why does <code>Get\u00adModule\u00adInformation<\/code> return <code>NULL<\/code> for executables?<\/p>\n<p>Internally, <code>Get\u00adModule\u00adInformation<\/code> walks the loader&#8217;s internal list of loaded modules and returns the entry point from the loader entry for said module. If you load an executable via <code>Load\u00adLibrary<\/code>, the entry point entry in the loader data structure will not be set.<\/p>\n<p>The loader doesn&#8217;t record the entry point for executable modules because executable modules don&#8217;t receive <code>DLL_<wbr \/>PROCESS_<wbr \/>ATTACH<\/code> notifications. The entry point for an executable module is the process entry point, not the <code>Dll\u00adMain<\/code> function. (The loader also doesn&#8217;t record the entry point for CLR DLLs since that entry point is a dummy function that crashes on purpose.)<\/p>\n<p>Basically, the deal is that <code>Get\u00adModule\u00adInformation<\/code> is in <code>PSAPI.DLL<\/code>, and <code>PSAPI.DLL<\/code> goes &#8220;I&#8217;m in ur process steeling ur data&#8221;. Not only does the loader have no use for entry points of EXEs and CLR DLLs, it explicitly <i>must not<\/i> call those entry points, so it sets the entry point to null to mean &#8220;Don&#8217;t call this guy.&#8221; This is a private thing inside the loader. <code>PSAPI.DLL<\/code> goes in and steals it and says &#8220;Hey check out all this good stuff I stole!&#8221; You&#8217;re basically getting grey market data.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Because it&#8217;s getting the information from the loader, who has no use for such things.<\/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-105800","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Because it&#8217;s getting the information from the loader, who has no use for such things.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105800","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=105800"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105800\/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=105800"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105800"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105800"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}