{"id":112731,"date":"2026-09-25T07:00:00","date_gmt":"2026-09-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112731"},"modified":"2026-09-25T19:59:10","modified_gmt":"2026-09-26T02:59:10","slug":"20260925-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260925-00\/?p=112731\/","title":{"rendered":"Debugging walkthrough: Access violation on nonsense instruction, episode 3"},"content":{"rendered":"<p>A customer reported that their employees were randomly getting &#8220;memory write errors&#8221;.<\/p>\n<p>This was their way of interpreting <a title=\"Why does the access violation error message put the operation in quotation marks, redux\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20151106-00\/?p=92012\"> the error message<\/a><\/p>\n<blockquote class=\"q\"><p>The instruction at &#8220;XX&#8221; referenced memory at &#8220;YY&#8221;. The memory could not be &#8220;written&#8221;.<\/p><\/blockquote>\n<p>Okay, so what we have here is an access violation.<\/p>\n<p>The strange thing was that this access violation was happening across multiple unrelated programs, rather than all occurring in a single program or family of programs. So there is some sort of broader problem here, rather than just a single buggy program.<\/p>\n<p>Opening one of the crash dumps shows this:<\/p>\n<pre>eax=0013d354 ebx=0049a000 ecx=009e9a9f edx=03111160 esi=009e9aa0 edi=009e9aa0\r\neip=009e9aa7 esp=003cfda4 ebp=003cfdb0 iopl=0         nv up ei pl nz ac pe cy\r\ncs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010217\r\nWerFault!wmainCRTStartup+0x7:\r\n009e9aa7 0000            add     byte ptr [eax],al          ds:002b:0013d354=??\r\n0:000&gt;\r\n<\/pre>\n<p>That <code>add byte ptr [eax], al<\/code> should immediately tell you that we are not executing valid code: <a title=\"Debugging walkthrough: Access violation on nonsense instruction, episode 2\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150313-00\/?p=44473\"> It is the instruction that you get if you try to execute zeroes<\/a>. You can see the zeroes in the second column.<\/p>\n<p>All of the crash dumps look like this, just with different process names.<\/p>\n<p>Let&#8217;s disassemble from the start of the function to see how we got here.<\/p>\n<pre>0:000&gt; u .-7\r\nWerFault!wmainCRTStartup:\r\n009e9aa0 90              nop\r\n009e9aa1 49              dec     ecx\r\n009e9aa2 ba6011f102      mov     edx,2F11160h\r\n009e9aa7 0000            add     byte ptr [eax],al \u2190 died here\r\n009e9aa9 0000            add     byte ptr [eax],al\r\n009e9aab 41              inc     ecx\r\n009e9aac ffe2            jmp     edx\r\n009e9aae cc              int     3\r\n<\/pre>\n<p>This doesn&#8217;t look like the proper start of a function.<\/p>\n<p>I mean, one clue is that it starts with a single-byte <code>nop<\/code>, rather than a <a title=\"Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110921-00\/?p=9583\"> <code>mov edi, edi<\/code>, as is customary for x86-32 code<\/a>.\u00b9<\/p>\n<p>And then of course there is the chunk of four <code>00<\/code> bytes in the middle of the instruction stream.<\/p>\n<p>What I thought was interesting is that if you take out those four <code>00<\/code> bytes, then the <code>dec ecx<\/code> and <code>inc ecx<\/code> cancel out, and what&#8217;s left looks like a detour: It loads an absolute address into <code>edx<\/code> and then jumps to it.<\/p>\n<p>This looks to me like a failed attempt to detour the function. The next step is to try to figure out what they were trying to do.<\/p>\n<p>Well, it looks like it&#8217;s trying to detour to a function at <code>0x02f11160<\/code>, so let&#8217;s see what the debugger can tell us about that.<\/p>\n<pre>0:000&gt; !address 0x2f11160\r\n\r\nUsage:              &lt;unknown&gt;\r\nBase address:       02f11000\r\nEnd address:        02f12000\r\nRegion Size:        00001000 (4.000 kB)\r\nState:              00001000 MEM_COMMIT\r\nProtect:            00000020 PAGE_EXECUTE_READ\r\nType:               00020000 MEM_PRIVATE\r\nAllocation Base:    02f10000\r\nAllocation Protect: 00000004 PAGE_READWRITE\r\n<\/pre>\n<p>So this is a mystery 4KB allocation of executable memory.<\/p>\n<p>Maybe there are some interesting strings in that memory block.<\/p>\n<pre>0:000&gt; !strings 02f11000 02f12000\r\n02f11080 --------\r\n02f11148 ----------------\r\n02f11472 C:\\Program Files\\Common Files\\Contoso\\injcore.dll\r\n02f11545 IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\r\n<\/pre>\n<p>Okay, well, that path to a DLL kind of catches them red-handed. I bet the &#8220;inj&#8221; stands for &#8220;injection&#8221;. But what were they trying to do?<\/p>\n<p>I figured, &#8220;Hm, the extra four zero bytes come right after the constant they were trying to load, so if I change the <code>mov edx<\/code> to a <code>mov rdx<\/code>, it would be the upper half of a 64-bit constant, and then this would look okay again.<\/p>\n<p>Now, this is a 32-bit process (evidenced by the 32-bit instruction pointer), so there is no <code>mov rdx<\/code> instruction. That instruction requires a 64-bit process.<\/p>\n<p>But wait, what if they got confused and <i>thought<\/i> it was a 64-bit process?<\/p>\n<p>Let&#8217;s disassemble these bytes as if they had been injected into a 64-bit process.<\/p>\n<p>The way I do this is to load up a sacrificial 64-bit debug session and just patch into it the bytes that I want to study. For poetic irony, I will load the 64-bit <tt>WerFault.exe<\/tt> into the debugger as a dump file.<\/p>\n<pre>C:\\&gt; windbgx -z C:\\Windows\\System32\\WerFault.exe\r\n\r\nExecutable search path is: \r\nModLoad: 00000001`40000000 00000001`400a1000   C:\\Windows\\System32\\WerFault.exe\r\nWerFault!wmainCRTStartup:\r\n00000001`40002480 sub     rsp,28h\r\n0:000&gt; eb . 90 49 ba 60 11 f1 02 00 00 00 00 41 ff e2 cc\r\n0:000&gt; u .\r\nWerFault!wmainCRTStartup\r\n00000001`40002480 nop\r\n00000001`40002481 mov     r10,2F11160h\r\n00000001`4000248b jmp     r10\r\n00000001`4000248e int     3\r\n<\/pre>\n<p>Okay, now it makes much more sense. This is a 64-bit detour that loads an absolute jump target into a 64-bit register (<code>r10<\/code>) and then jumps to it.<\/p>\n<p>They injected 64-bit code into a 32-bit process!<\/p>\n<p>This also explains why the crashes are sporadic: The customer&#8217;s employees run 64-bit processes most of the time, but on occasion, something will run a 32-bit process, and those are the ones that are crashing.<\/p>\n<p>Upon further discussion with the customer, we learned that Contoso is an anti-malware program that they use. <a title=\"Tricks from product support: We're not smart enough to debug the problem, can you help us?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241203-00\/?p=110601\"> We advised them to disable it temporarily<\/a> to confirm that it was the source of the problem, but they didn&#8217;t want to disable their anti-malware software.<\/p>\n<p>Okay, so we advised them to check with the vendor to see if an update is available. They were resistant to changing their anti-malware software without first putting it through their internal validation. They considered this a Windows problem, and they demanded a Windows solution.<\/p>\n<p>We are still working to convince the customer that they need to re-evaluate their anti-malware software.\u00b2<\/p>\n<p>\u00b9 Even if this were a 64-bit process, <a title=\"Why don't Windows functions begin with a pointless MOV EDI,EDI instruction on x86-64?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221109-00\/?p=107373\"> Windows components don&#8217;t begin functions with a single-byte <code>nop<\/code> or any other single-byte instruction<\/a>. It would use a two-byte <code>nop<\/code> if it uses one at all.<\/p>\n<p>\u00b2 This is a downside of communicating with the customer through a customer liaison: My colleague explained that relaying this level of detail through a customer liaison who is not sufficiently technical to be familar with debugging puts us at a disadvantage because the liaison can&#8217;t stand up to the customer pushback. This is a case where we may have to let the engineers talk directly to the customer.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Trying to figure out what the interloper was trying to.<\/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-112731","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Trying to figure out what the interloper was trying to.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112731","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=112731"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112731\/revisions"}],"predecessor-version":[{"id":112732,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112731\/revisions\/112732"}],"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=112731"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112731"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112731"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}