Saturday, February 1, 2014

Lobotomizing Quicken 2007

I am an old-school Quicken user, back to the Apple II days. Intuit used to be OK, but as the Mac declined in the 90s, Intuit started ignoring it. Every year they would throw a few more features into Quicken that no one cared about, and everyone would upgrade.

Bank connectivity was simple; you downloaded files from your bank in the QIF format, and you reconciled things. But QIF wasn't a great file format. There was an open file standard called OFX that Microsoft Money used, but Intuit didn't see the appeal in that. Intuit took OFX, added a custom field to it called "INTU.BID", called the file format QFX, and started charging banks to get a number to put into that field. And because Quicken now worked online, Intuit could enforce the payment from banks. If banks didn't pay, Quicken wouldn't know about their "INTU.BID", and Quicken wouldn't download their customers' data.

I don't know if that's legally extortion, but I'm going to call it that. And so do lots of other unhappy people.

Meanwhile, Intuit continued to screw over Mac users. The last version of Quicken that Intuit released for the Mac was Quicken 2007. It was a PowerPC app. In 2009 they announced a new app, "Quicken Essentials for the Mac", which was released in February of 2010. It was an under-powered joke that didn't do things like track investments, so Intuit suggested that people who needed to do more than balance their checkbook keep using Quicken 2007.

When Mac OS X Lion came out, and Quicken 2007 wouldn't run any more because it was a PowerPC app, Intuit acted swiftly. Lion came out in July of 2011, and seven months later in March of 2012 Intuit released a crappy recompilation into x86.

And that's it.

Why do I bring this up? Because I hate Intuit. I despise them because they have made it abundantly clear that they despise me and all of their customers.

A few months ago, I signed up for the computer-driven investment manager Wealthfront, started by people who wanted to use high tech to bring quality investing to non-finance minded people (e.g. me). (I think they're cool enough to recommend; I'd like it if you were to sign up with my referral link so that both you and I save money, though it's your call and I won't mind it if you don't.)

But Wealthfront uses Apex Clearing to hold their accounts, and while Apex paid their Intuit tax and offers QFX downloads, guess what? They paid for Windows but not Mac. And so Mac Quicken won't import their files.

Now, ever since Intuit pulled the INTU.BID stunt with QFX, people have been trying to work around it. If you google around, you'll find the usual suggestion is to switch out the INTU.BID number for one that works. I tried that, but couldn't get it to work.

And then I asked myself, why don't I fix Quicken itself?

When you open an unauthorized QFX file, you get the "unable to verify the Financial Institution information" dialog. So I took a stack trace, and that dialog is fired from:

QApplication::OpenQuickenDoc(short, long, char*, unsigned long, long) + 225  [0x278b45]
QApplication::OpenWebConnectFile(short, long, char*) + 186  [0x278a34]
QMSession::ProcessWebConnectFile(char const*) + 306  [0x20e23e]
QMSession::SendSessions(short&, StretchyString&) + 2392  [0x20ec4c]
SessionMgr::StartLocalMsgSetSessions(unsigned short, EBVoidPtrArray*, SendQ*, short, unsigned long) + 180  [0x4813ca]
MsgSetConnectionMgr::CreateLocalSessions(unsigned short, EBVoidPtrArray*, SessionList*) + 1227  [0x3fe969]
QMSession::BeginMessageSet(unsigned short, OEMessageSetRs*, unsigned long, OEString const&) + 79  [0x20f19b]
QImportOFXRs::PrepareForSession(OEString const&) + 722  [0x212f6e]
DoErrorMsg(char*, char*, OpaqueWindowPtr*, AlertFunctor*) + 39  [0x38d7fc]

Note that they ship their binary with symbols. Really. This made my job a million times easier.

So I went to look at QImportOFXRs::PrepareForSession. When I tossed Quicken into Hopper and went to that function, I saw:

Look at the call to EBCache::GetWebConnectInfoForFeature. Quicken tests to make sure it has web connect info, and if it doesn't have that info it throws up the error dialog and dies.

But what's interesting is that, at 212f73, it immediately overwrites eax. In other words, Quicken tests to make sure it has web connect info for the bank, and then immediately throws out that information. So what happens if I NOP-out that entire error block, so that Quicken always proceeds?

It works. Quicken puts "FI needs update" as the bank name, but it allows you to proceed with the import.

There is a problem, though. I experimented a bit, and found that if you do a partial import, and then try to go back to the "Download Transactions..." window, Quicken will say that you need to update your bank list. And if you allow it to do so, it realizes the tricks you've pulled, and disables your account from ever importing again.

That particular dialog, the "you had an online account for a bank we don't know about any more, so we're going to disable it" one, comes from the stack trace:

OldQuickenMenuGlue::DoCommandProcess(PPx::SysCarbonEvent&, HICommand, unsigned long, unsigned long) + 156  [0x1b2610]
MenuManager::DoCommand(long) + 107  [0x1b1173]
OnlineMenuHandler::DoCommand(short) + 75  [0x1b5503]
needsToUpdateBankList() + 579  [0x1b5e07]
DoAlert(short, unsigned char const*, unsigned char const*, unsigned char const*, unsigned char const*, unsigned char const*, short, short, short, OpaqueWindowPtr*, AlertFunctor*) + 898  [0xd6d51]
DoAlert(short, __CFString const*, __CFString const*, __CFString const*, __CFString const*, __CFString const*, __CFString const*, __CFString const*, AlertButton, AlertButton, OpaqueWindowPtr*, AlertFunctor*) + 215  [0xd69c3]

Looking, it seems like needsToUpdateBankList is a bad function to ever be in. No. We should never want to update the bank list.

Fortunately, that's easy.

I overwrote the first few bytes of the function with 33C0C3 which is xor eax, eax; ret.

I don't know if I'm done. Who knows what other subtle pitfalls I might need to disarm that I don't yet know about? But I have a working import:

and right now that's all that matters.

(Note: All offsets are as of Quicken 2007 version 16.1.4 (build 23.880.400). Your mileage may vary. This will probably void your warranty and possibly your EULA. Please take the usual precautions, including backing up your original application binary.)

2021 Edit: A friend Greg notes:
Thanks for sharing the link with your spelunking adventures. It prompted me to perform surgery on my own copy of Q2007 (version 16.2.4 (25.886.100)), which, of course, has different offsets within the code for the surgical procedures. Just in case you wanted to update your page, the changes I made are:

at offset 2177598 is the sequence 0x7543 (jne opcode with offset), which I changed to 0xEB43 (jmp opcode with offset). I did this rather than nop out the code block as you did, just because I feel that the fewer changes the better.

at offset 1795460 is the sequence 0x5589E5, which I changed to 0x33C0C3 (as you did).

So far, so good, although I admit that I haven't yet performed any testing. (It still runs, but I haven't tried to import any .QFX files.)

No comments:

Post a Comment