{"id":45581,"date":"2015-05-18T07:00:00","date_gmt":"2015-05-18T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20150518-00\/?p=45581\/"},"modified":"2019-03-13T12:15:30","modified_gmt":"2019-03-13T19:15:30","slug":"20150518-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150518-00\/?p=45581","title":{"rendered":"Determining programmatically whether a file was built with LAA, ASLR, DEP, or OS-assisted \/GS"},"content":{"rendered":"<p>Today&#8217;s Little Program parses a module to determine whether or not it was built with the following flags: <\/p>\n<ul>\n<li><a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/wz223b1z.aspx\"><code>\/LARGE&shy;ADDRESS&shy;AWARE<\/code><\/a> \n<li><a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb384887.aspx\"><code>\/DYNAMIC&shy;BASE<\/code><\/a>,     also known as Address Space Layout Randomization (ASLR) \n<li><a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dn195771.aspx\"><code>\/HIGH&shy;ENTROPY&shy;VA<\/code><\/a>, or 64-bit ASLR,     which I like to call ASLRR (the extra R is for extra random) \n<li><a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms235442.aspx\"><code>\/NX&shy;COMPAT<\/code><\/a>,     also known as Data Execution Prevention (DEP) \n<li><a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/8dbf701c.aspx\"><code>\/GS<\/code><\/a>, which turns on certain runtime buffer overrun checks. <\/ul>\n<p>Remember, Little Programs do little error checking. In particular, this Little Program does no range checking, so a malformed binary can result in wild pointers. <\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;imagehlp.h&gt;\n#include &lt;stdio.h&gt; \/\/ horrors! mixing stdio and C++!\n#include &lt;stddef.h&gt;\n\nclass MappedImage\n{\npublic:\n bool MapImage(const char* fileName);\n void ProcessResults();\n ~MappedImage();\n\nprivate:\n WORD GetCharacteristics();\n\n template&lt;typename T&gt;\n WORD GetDllCharacteristics();\n\n template&lt;typename T&gt;\n bool HasSecurityCookie();\n\nprivate:\n HANDLE file_ = INVALID_HANDLE_VALUE;\n HANDLE mapping_ = nullptr;\n void *imageBase_ = nullptr;\n IMAGE_NT_HEADERS* headers_ = nullptr;\n int bitness_ = 0;\n};\n\nbool MappedImage::MapImage(const char* fileName)\n{\n file_ = CreateFile(fileName, GENERIC_READ,\n    FILE_SHARE_READ,\n    NULL,\n    OPEN_EXISTING,\n    0,\n    NULL);\n if (file_ == INVALID_HANDLE_VALUE) return false;\n\n mapping_ = CreateFileMapping(file_, NULL, PAGE_READONLY,\n                              0, 0, NULL);\n if (!mapping_) return false;\n\n imageBase_ = MapViewOfFile(mapping_, FILE_MAP_READ, 0, 0, 0);\n if (!imageBase_) return false;\n\n headers_ = ImageNtHeader(imageBase_);\n if (!headers_) return false;\n if (headers_-&gt;Signature != IMAGE_NT_SIGNATURE) return false;\n\n switch (headers_-&gt;OptionalHeader.Magic) {\n case IMAGE_NT_OPTIONAL_HDR32_MAGIC: bitness_ = 32; break;\n case IMAGE_NT_OPTIONAL_HDR64_MAGIC: bitness_ = 64; break;\n default: return false;\n }\n\n return true;\n}\n\nMappedImage::~MappedImage()\n{\n if (imageBase_) UnmapViewOfFile(imageBase_);\n if (mapping_) CloseHandle(mapping_);\n if (file_ != INVALID_HANDLE_VALUE) CloseHandle(file_);\n}\n\nWORD MappedImage::GetCharacteristics()\n{\n return headers_-&gt;FileHeader.Characteristics;\n}\n\ntemplate&lt;typename T&gt;\nWORD MappedImage::GetDllCharacteristics()\n{\n  return reinterpret_cast&lt;T*&gt;(headers_)-&gt;\n    OptionalHeader.DllCharacteristics;\n}\n\ntemplate&lt;typename T&gt;\nbool MappedImage::HasSecurityCookie()\n{\n ULONG size;\n T *data = static_cast&lt;T*&gt;(ImageDirectoryEntryToDataEx(\n    imageBase_, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,\n    &amp;size, NULL));\n if (!data) return false;\n ULONG minSize = offsetof(T, SecurityCookie) +\n                 sizeof(data-&gt;SecurityCookie);\n if (size &lt; minSize) return false;\n if (data-&gt;Size &lt; minSize) return false;\n return data-&gt;SecurityCookie != 0;\n}\n\nvoid MappedImage::ProcessResults()\n{\n printf(\"%d-bit binary\\n\", bitness_);\n auto Characteristics = GetCharacteristics();\n printf(\"Large address aware: %s\\n\",\n    (Characteristics &amp; IMAGE_FILE_LARGE_ADDRESS_AWARE)\n    ? \"Yes\" : \"No\");\n\n auto DllCharacteristics = bitness_ == 32\n    ? GetDllCharacteristics&lt;IMAGE_NT_HEADERS32&gt;()\n    : GetDllCharacteristics&lt;IMAGE_NT_HEADERS64&gt;();\n\n printf(\"ASLR: %s\\n\",\n    (DllCharacteristics &amp; IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)\n    ? \"Yes\" : \"No\");\n printf(\"ASLR^2: %s\\n\",\n    (DllCharacteristics &amp; IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA)\n    ? \"Yes\" : \"No\");\n printf(\"DEP: %s\\n\",\n    (DllCharacteristics &amp; IMAGE_DLLCHARACTERISTICS_NX_COMPAT)\n    ? \"Yes\" : \"No\");\n printf(\"TS Aware: %s\\n\",\n    (DllCharacteristics &amp; IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE)\n    ? \"Yes\" : \"No\");\n\n bool hasSecurityCookie =\n    bitness_ == 32 ? HasSecurityCookie&lt;IMAGE_LOAD_CONFIG_DIRECTORY32&gt;()\n                   : HasSecurityCookie&lt;IMAGE_LOAD_CONFIG_DIRECTORY64&gt;();\n printf(\"\/GS: %s\\n\", hasSecurityCookie\n    ? \"Yes\" : \"No\");\n}\n\nint __cdecl main(int argc, char**argv)\n{\n MappedImage mappedImage;\n if (mappedImage.MapImage(argv[1])) {\n  mappedImage.ProcessResults();\n }\n return 0;\n}\n<\/pre>\n<p>Let&#8217;s see what happened. <\/p>\n<p>First we use the <code>Map&shy;Image<\/code> method to load the binary and map it into memory. While we&#8217;re at it, we sniff at the headers to determine whether it is a 32-bit or 64-bit binary. <\/p>\n<p>The <code>Get&shy;Characteristics<\/code> method merely extracts the <code>Characteristics<\/code> from the <code>File&shy;Header<\/code>. This is easy because the <code>File&shy;Header<\/code> is the same for 32-bit and 64-bit binaries. <\/p>\n<p>The <code>Get&shy;Dll&shy;Characteristics<\/code> method has two versions depending on the image bitness. In both cases, it extracts the <code>Dll&shy;Characteristics<\/code> field, but the location of the field depends on the structure. <\/p>\n<p>The <code>Has&shy;Security&shy;Cookie<\/code> method also has two versions depending on the image bitness. The minimum size necessary to get OS-assisted stack overflow protection is the size that encompasses the <code>Security&shy;Cookie<\/code> member, and in order to get that extra protection, the member needs to be nonzero. <\/p>\n<p>What is OS-assisted stack overflow protection? <\/p>\n<p>First, I&#8217;m going to assume that you&#8217;ve read <a HREF=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=7260\">Compiler Security Checks In Depth<\/a>. <\/p>\n<p>Okay, welcome back. <\/p>\n<p>In theory, <code>\/GS<\/code> could be implemented entirely in application code, with no need for operating system assistance. And in fact, that&#8217;s what happens when the executable is run on older versions of Windows (like Windows&nbsp;98 or Windows&nbsp;2000). But the module can tell the operating system, &#8220;Hey, here is where I put my security cookie,&#8221; and if the operating system understands this field, then it will go in and make the security cookie even more randomer than random by mixing in some cryptographically secure random bits. <\/p>\n<p>Okay, so that&#8217;s the program. Note that <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2014\/05\/02\/10522232.aspx\">some of these flags are meaningless in DLLs<\/a>, so be careful to interpret the output correctly. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sucking the flags out of the header.<\/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-45581","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Sucking the flags out of the header.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/45581","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=45581"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/45581\/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=45581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=45581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=45581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}