Friday, December 12, 2008

Beware of sysaudio.sys!

There's a new Google hijacker running around that can install even on fully updated copies of Firefox 2. The hijacker in question drops a file named sysaudio.sys in the %SystemRoot%\system32 directory (not to be confused with the one in %SystemRoot%\system32\drivers). When this file is loaded, it silently hooks the ws2_32.dll calls made by web browsers and watches for requests to search engines. When it sees them, it pulls fake search results through script injection from http://1.2.3.0, which is a trampoline to another site. The version that infected my SO's computer happened to go to 94.247.2.58 (hs.2-58.zlkon.lv -- *.zlkon.lv being a known purveyor of malware and spam). The file doesn't appear to do anything else, short of being a little interesting if you open it in notepad (some of its imports are repeated backwards).

The big threat here isn't the search results, which are basically useless and an annoyance, but rather the fact that the owner of that site could easily alter it to deliver much more devastating software. There's also the little problem that there's not a lot of information on this running around and that major AV packages (F-Secure and NOD32) don't see anything wrong with the file. Malwarebyte's Anti-Malware detects it as "Rootkit.Agent," though it doesn't seem to have any rootkit properties (no kernel hooks, for instance, at least not detectable by Rootkit Unhooker).

So, watch out for this file, and consider blocking the above address at your router. As I noted, it can drive-by install on a fully patched Firefox 2. Whether DEP can stop it, I don't know; the machine I found it on only supports software DEP, and it was in opt-in mode.

UPDATE: Firefox 3 is vulnerable to this as well. I don't believe Chrome is, however.

Tuesday, December 9, 2008

OnDeserializationAttribute, Dictionary members, and GetHashCode

As it turns out, the Dictionary<T, U> generic collection in the .NET Framework uses custom serialization and builds itself in its OnDeserialized method. What this means is that, if you deserialize such a Dictionary (using any of the Formatters), it will proceed to deserialize its keys and values and add them back to itself. No problems there, right?

What's a bit surprising is that the keys and values do not have any of their custom serialized members (such as, oh, their own generic Dictionary members) deserialized before that happens. This could prove a real problem for you if you override object.Equals, implement IEquatable<T>, and override GetHashCode. When a key is added to a Dictionary, it is added by hashcode (no surprise). That hashcode is then immutable, at least as far as the Dictionary is concerned. Changes to the key that alter the hashcode do not change the hashcode for the object that the Dictionary remembers. Normally, that's not a problem, but some methods, such as Dictionary<T, U>.ContainsKey, manage their fast operation by comparing hashcodes -- in the case of that method, it looks for a key with the right hashcode, then calls key.Equals(candidate). These methods, therefore, will not perform as expected if modifications are made to the keys that affect their hashcode. Optimally, the keys should be immutable.

All discussion of the innards of the Dictionary class aside, the real point here is that if you have keys in a Dictionary that are mutable objects (let's call instances of class Entity) whose hashcodes can change based on the values of their members, and any of those members are themselves Dictionaries, the hashcodes that the Dictionary<Entity, U> object stores will be incorrect on deserialization. At the time the Dictionary of Entities is deserialized, the Dictionary members of the Entity instance have not yet been deserialized, so the Entity object's hashcode is incorrect given its actual data. You'll be in the fun position of having keys that are in your Dictionary (via the Keys property) appear as not being in your Dictionary (according to ContainsKey). This can be a real pain to debug, especially if you're working in ASP.NET and you have relatively limited visibility into the automated (de)serialization process.

In the case above, the solution is to add an OnDeserialization method to your Entity class and mark it with the OnDeserialization attribute. In that method, you should call the OnDeserialization method for each of your dictionaries. Currently, the implementation of the Framework is such that your OnDeserialization method will be called before the Dictionary calls GetHashCode on the instance (thankfully, as it wouldn't make sense any other way), and your hashcodes will be correct.

(See http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=322952 for Microsoft's confirmation of this behavior and solution.)

Tuesday, December 2, 2008

Don't forget to surface BAPI_TRANSACTION_*!

When working with SAP and the BizTalk Adapter Pack, you've got a lot of opportunities to make mistakes -- especially when working in RFC mode, the mode that's recommended by Microsoft (and, to a certain extent, me, as it makes life easier having all your BAPIs on one object and being able to combine multiple BAPIs into one transaction). One newbie mistake is forgetting to surface BAPI_TRANSACTION_COMMIT and BAPI_TRANSACTION_ROLLBACK. Don't forget these guys! Many, if not most, BAPI functions in modern versions of SAP don't auto-commit, and you'll be left wondering why SAP said your changes were saved and documents were created but nothing shows up in your installation if you don't remember to surface and call these BAPIs as necessary. "As necessary" means checking the Messages in the returned BAPIRET2 table and looking for messages with no TYPE or a TYPE of "S" (for Success, naturally), then calling the rollback BAPI if you find some messages that don't fall into that category. Otherwise, you should call the commit BAPI.*

Oh, you're probably wondering what BAPIs and RFC mode are. Those will have to wait for another day.

* Note that SAP can generate some warnings and still succeed in performing the task. This is common when working with Sales Orders if two Customer PO Numbers (BAPISDHD1.PURCH_NO_C) are the same. Also, SAP can fail to perform a task without generating any error message; this is common when working with documents associated with the Business Object, such as SAPTexts or Order Schedules. If one of those fails to create (because of incorrect parameters or insufficient information), you won't receive any message about it at all. Otherwise, you'll receive a Success message. I'll have more on SAP's "error information" -- and those are massive sarcasm quotes -- at a later date.