{"id":6103,"date":"2012-11-13T07:00:00","date_gmt":"2012-11-13T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/11\/13\/microsoft-money-crashes-during-import-of-account-transactions-or-when-changing-a-payee-of-a-downloaded-transaction\/"},"modified":"2012-11-13T07:00:00","modified_gmt":"2012-11-13T07:00:00","slug":"microsoft-money-crashes-during-import-of-account-transactions-or-when-changing-a-payee-of-a-downloaded-transaction","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20121113-00\/?p=6103","title":{"rendered":"Microsoft Money crashes during import of account transactions or when changing a payee of a downloaded transaction"},"content":{"rendered":"<blockquote CLASS=\"q\"><p>\n<b>Update<\/b>:\nAn official fix for this issue has been released to Windows Update,\nalthough I must say that I think my patch has more style than the\nofficial one.\nYou do not need to patch your binary.\nJust keep your copy of Windows&nbsp;8 up to date and you&#8217;ll be fine.\n<\/p><\/blockquote>\n<p>\nFor the five remaining Microsoft Money holdouts\n(meekly raises hand),\nhere&#8217;s a patch for a crashing bug during import of account transactions\nor when changing a payee of a downloaded transaction\nin\n<a HREF=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=20738\">\nMicrosoft Money Sunset Deluxe<\/a>.\nPatch the mnyob99.dll file as follows:\n<\/p>\n<ul>\n<li>File offset 003FACE8: Change 85 to 8D\n<li>File offset 003FACED: Change 50 to 51\n<li>File offset 003FACF0: Change FF to 85\n<li>File offset 003FACF6: Change E8 to B9\n<\/ul>\n<p>\nNote that this patch is\n<b>completely unsupported<\/b>.\nIf it makes your computer explode or transfers all your money\nto an account in the Cayman Islands,\nwell, too bad for you.\n<\/p>\n<p>\nIf you are not one of the five remaining customers of Microsoft Money,\nthis is a little exercise in\napplication compatibility debugging.\nWhy application compatibility debugging?\nBecause the problem seems to be more prevalent\non Windows&nbsp;8 machines.\n<\/p>\n<p>\nNote that I used no special knowledge about Microsoft Money.\nAll this debugging was performed with information you also have\naccess to.\nIt&#8217;s not like I have access to the Microsoft Money source code.\nAnd I did this debugging entirely on my own.\nIt was not part of any official customer support case or anything like that.\nI was just debugging a crash that I kept hitting.\n<\/p>\n<p>\nThe crash occurs in the function <code>utlsrf08!DwStringLengthA<\/code>:\n<\/p>\n<pre>\nutlsrf08!DwStringLengthA:\n        push    ebp\n        mov     ebp,esp\n        mov     eax,dword ptr [ebp+8]\n        lea     edx,[eax+1]\nagain:\n        mov     cl,byte ptr [eax]\n        inc     eax\n        test    cl,cl\n        jne     again\n        sub     eax,edx\n        pop     ebp\n        ret     4\n<\/pre>\n<p>\nThe proximate cause is that the string pointer in <code>eax<\/code>\nis garbage.\nIf you unwind the stack one step, you&#8217;ll see that the\npointer came from here:\n<\/p>\n<pre>\n        lea     eax,[ebp-20Ch]\n        push    eax\n        call    dword ptr [__imp__GetCurrentProcessId]\n        push    eax\n        push    offset \"Global\\TRIE@%d!%s\"\n        lea     eax,[ebp-108h]\n        push    104h\n        push    eax\n        call    mnyob99!DwStringFormatA\n        add     esp,14h\n        lea     eax,[ebp-2E4h]\n        push    eax\n        push    5Ch\n        <font COLOR=\"red\">push    dword ptr [ebp-2E4h]<\/font> ; invalid pointer\n        call    mnyob99!DwStringLengthA\n        sub     eax,7\n        push    eax\n        lea     eax,[ebp-101h]\n        push    eax\n        jmp     l2\nl1:\n        mov     eax,dword ptr [ebp-2E4h]\n        mov     byte ptr [eax],5Fh\n        lea     eax,[ebp-2E4h]\n        push    eax\n        push    5Ch\n        push    dword ptr [ebp-2E4h]\n        call    mnyob99!DwStringLengthA\n        push    eax\n        push    dword ptr [ebp-2E4h]\nl2:\n        call    mnyob99!FStringFindCharacterA\n        cmp     dword ptr [ebp-2E4h],edi\n        jne     l1\n<\/pre>\n<p>\nI was lucky in that all the function calls here were to imported\nfunctions, so I could extract the names from the imported function table.\nFor example, the call to <code>DwStringFormatA<\/code> was originally\n<\/p>\n<pre>\n        call    mnyob99!CBillContextMenu::SetHwndNotifyOnGoto+0x1e56a (243fc3cc)\n<\/pre>\n<p>\nBut the target address is an\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/07\/26\/679044.aspx\">\nimport stub<\/a>:\n<\/p>\n<pre>\n        jmp     dword ptr [mnyob99+0x1ec0 (24001ec0)]\n<\/pre>\n<p>\nAnd then I can walk the import table to see that this was the import\ntable entry for <code>utlsrf08!DwStringFormatA<\/code>.\nFrom the function name, it&#8217;s evident that this is some sort of\n<code>sprintf<\/code>-like function.\n(If you disassemble it, you&#8217;ll see that it&#8217;s basically\na wrapper around\n<code>vsnprintf<\/code>.)\n<\/p>\n<p>\nReverse-compiling this code, we get\n<\/p>\n<pre>\nchar name[...];\nchar buffer[MAX_PATH];\nchar *backslash;\n...\nDwStringFormatA(buffer, MAX_PATH, \"Global\\\\TRIE@%d!%s\",\n                GetCurrentProcessId(), name);\n\/\/ Change all backslashes (except for the first one) to underscores\nif (FStringFindCharacterA(buffer + 7, DwStringLengthA(backslash) - 7,\n                          '\\\\',&amp;backslash))\n{\n  do {\n    *backslash = '_'; \/\/ Change backslash to underscore\n  } while (FStringFindCharacterA(backslash, DwStringLengthA(backslash),\n                                 '\\\\',&amp;backslash));\n}\n<\/pre>\n<p>\n(Remember, all variable names are made-up since I don&#8217;t have source\ncode access. I&#8217;m just working from the disassembly.)\n<\/p>\n<p>\nAt this point, you can see the bug:\nIt&#8217;s an uninitialized variable at the first call to\n<code>String&shy;Find&shy;CharacterA<\/code>.\nWhether we crash or survive is a matter of luck.\nIf the uninitialized variable happens to be a pointer\nto readable data,\nthen the\n<code>Dw&shy;String&shy;LengthA<\/code> will eventually\nfind the null terminator,\nand since in practice the string does not contain\nany extra backslashes,\nthe call to\n<code>FString&shy;Find&shy;CharacterA<\/code> fails,\nand nobody gets hurt.\n<\/p>\n<p>\nBut it looks like their luck ran out,\nand now the uninitialized variable contains\nsomething that is not a valid pointer.\n<\/p>\n<p>\nThe <code>if<\/code> test should have been\n<\/p>\n<pre>\nif (FStringFindCharacterA(buffer + 7, DwStringLengthA(buffer) - 7,\n                          '\\\\',&amp;backslash))\n<\/pre>\n<p>\nThis means changing the\n<\/p>\n<pre>\n        push    dword ptr [ebp-2E4h]\n<\/pre>\n<p>\nto\n<\/p>\n<pre>\n        lea     eax,[ebp-101h]\n        push    eax\n<\/pre>\n<p>\nUnfortunately, the patch is one byte larger than the existing\ncode, so we will need to get a little clever in order to get it\nto fit.\n<\/p>\n<p>\nOne trick is to rewrite the test as\n<\/p>\n<pre>\nif (FStringFindCharacterA(buffer + 7, DwStringLengthA(buffer + 7),\n                          '\\\\',&amp;backslash))\n<\/pre>\n<p>\nThat lets us rewrite the assembly code as\n<\/p>\n<pre>\n        lea     eax,[ebp-2E4h]\n        push    eax\n        push    5Ch\n        <font COLOR=\"blue\">lea     eax,[ebp-101h]          ; \\ was \"push dword ptr [ebp-2E4h]\"\n        push    eax                     ; \/\n        call    mnyob99!DwStringLengthA ; unchanged but code moved down one byte\n        nop                             ; \\ was \"sub eax,7\" (3-byte instruction)\n        nop                             ; \/<\/font>\n        push    eax\n        lea     eax,[ebp-101h]\n        push    eax\n<\/pre>\n<p>\nThe new instructions (<code>lea<\/code> and <code>push<\/code>)\nare one byte larger than the original <code>push<\/code>,\nbut we got rid of the three-byte <code>sub eax, 7<\/code>,\nso it&#8217;s a net savings of two bytes, which therefore fits.\n<\/p>\n<p>\nHowever, I&#8217;m going to crank the nerd level up another notch\nand try to come up with a patch that involves modifying\nas few bytes as possible.\nIn other words,\nI&#8217;m going for <i>style points<\/i>.\n<\/p>\n<p>\nTo do this, I&#8217;m going to take advantage of the fact that\nthe string length is the return value of\n<code>Dw&shy;String&shy;FormatA<\/code>,\nso that lets me eliminate the call to\n<code>Dw&shy;String&shy;LengthA<\/code> altogether.\nHowever, this means that I have to be careful not to\ndamage the value in <code>eax<\/code>\nbefore I get there.\n<\/p>\n<pre>\n        <font COLOR=\"blue\">lea     ecx,[ebp-2E4h] ; was \"lea eax,[ebp-2E4h]\"\n        push    ecx            ; was \"push eax\"<\/font>\n        push    5Ch\n        <font COLOR=\"blue\">nop                    ; \\\n        nop                    ; |\n        nop                    ; |\n        nop                    ; | was \"push dword ptr [ebp-2E4h]\"\n        nop                    ; |\n        nop                    ; \/\n        nop                    ; \\\n        nop                    ; |\n        nop                    ; | was \"call mnyob99!DwStringLengthA\"\n        nop                    ; |\n        nop                    ; \/<\/font>\n        sub     eax,7\n        push    eax\n        lea     eax,[ebp-101h]\n        push    eax\n<\/pre>\n<p>\nPatching the <cODE>lea eax, &#8230;<\/code> to be\n<code>lea ecx, ...<\/code> can be done with a single byte,\nand the <code>push eax<\/code> is a single-byte instruction\nas well, so the first two patches can be done with one byte each.\nThat leaves me with 11 bytes that need to be nop&#8217;d out.\n<\/p>\n<p>\nThe na&iuml;ve way of nopping out eleven bytes is simply\nto patch in 11 <code>nop<\/code> instructions,\nbut you can do better by taking advantage of the bytes\nthat are already there.\n<\/p>\n<pre>\n<font COLOR=\"red\">ff<\/font>b51cfdffff    push    dword ptr [ebp-2E4h]\n<font COLOR=\"blue\">85<\/font>b51cfdffff    test    dword ptr [ebp-2E4h],esi\n<font COLOR=\"red\">e8<\/font>770a0000      call    mnyob99!DwStringLengthA\n<font COLOR=\"blue\">b9<\/font>770a0000      mov     ecx,0A77h\n<\/pre>\n<p>\nBy patching a single byte in each of the two instructions,\nI can turn them into effective nops by making them do nothing\ninteresting.\nThe first one tests the uninitialized variable against some garbage bits,\nand the second one loads a unused register with a constant.\n(Since the <code>ecx<\/code> register is going to be trashed by the call to\n<code>FString&shy;Find&shy;CharacterA<\/code>,\nwe are free to modify it all we want prior to the call.\nNo code could have relied on it anyway.)\n<\/p>\n<p>\nThat second patch is a variation of one\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/11\/11\/255800.aspx\">\nI called out some time ago<\/a>,\nexcept that instead of patching out the call with a\n<code>mov eax, immed32<\/code>,\nwe&#8217;re using a\n<code>mov ecx, immed32<\/code>,\nbecause the value in the <code>eax<\/code> register is still important.\n<\/p>\n<p>\nHere&#8217;s the final result:\n<\/p>\n<pre>\n        <font COLOR=\"blue\">lea     ecx,[ebp-2E4h]           ; was \"lea eax,[ebp-2E4h]\"\n        push    ecx                      ; was \"push eax\"<\/font>\n        push    5Ch\n        <font COLOR=\"blue\">test    dword ptr [ebp-2E4h],esi ; was \"push dword ptr [ebp-2E4h]\"\n        mov     ecx,0a77h                ; was \"call mnyob99!DwStringLengthA\"<\/font>\n        sub     eax,7\n        push    eax\n        lea     eax,[ebp-101h]\n        push    eax\n<\/pre>\n<p>\n<b>Bonus chatter<\/b>:\nWhen I shared this patch with my friends,\nI mentioned that this patch made me feel like\nmy retired colleague\n<a HREF=\"http:\/\/hashable.org\/\">\nJeff<\/a>,\nwho had a reputation for accomplishing\nastonishing programming tasks\nin his spare time.\nYou would pop into his office asking for some help,\nand he&#8217;d fire up some program you&#8217;d never seen before.\n<\/p>\n<p>\n&#8220;What&#8217;s that?&#8221; you&#8217;d ask.\n<\/p>\n<p>\n&#8220;Oh, it&#8217;s a debugger I wrote,&#8221; he&#8217;d calmly reply.\n<\/p>\n<p>\nOr you&#8217;d point him to a program and apologize,\n&#8220;Sorry, I only compiled it for x86.\nThere isn&#8217;t an Alpha version.&#8221;\n<\/p>\n<p>\n&#8220;That&#8217;s okay, I&#8217;ll run it in my emulator,&#8221;\nhe&#8217;d say, matter-of-factly.\n<\/p>\n<p>\n(And retiring from Microsoft hasn&#8217;t slowed him down.\nHere&#8217;s\n<a HREF=\"http:\/\/jsmachines.net\/\">\nan IBM PC Model 5150 emulator written in JavaScript<\/a>.)\n<\/p>\n<p>\nSpecifically, I said,\n&#8220;I feel like Jeff, who does this sort of thing before\nhis morning coffee.&#8221;\n<\/p>\n<p>\nJeff corrected me.\n&#8220;If this was something I used to do before coffee,\nthat probably meant I was up all night.\nPersistence &gt;= talent.&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Update: An official fix for this issue has been released to Windows Update, although I must say that I think my patch has more style than the official one. You do not need to patch your binary. Just keep your copy of Windows&nbsp;8 up to date and you&#8217;ll be fine. For the five remaining Microsoft [&hellip;]<\/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-6103","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Update: An official fix for this issue has been released to Windows Update, although I must say that I think my patch has more style than the official one. You do not need to patch your binary. Just keep your copy of Windows&nbsp;8 up to date and you&#8217;ll be fine. For the five remaining Microsoft [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/6103","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=6103"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/6103\/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=6103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=6103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=6103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}