Wednesday, October 15, 2008

Kagi Registration Module (lack of) security

I'm in the process of choosing an eCommerce partner for selling my future shareware. I have narrowed down to eSellerate and Kagi as they are widely adopted by Mac shareware developers. After reading their respecting obfuscated pricing policies, I decided to have a look at what they offer for integrating the purchasing process into the application.

Kagi offers the Kagi Registration Module (KRM) which is basically a library that provides an in-application one click purchase experience. Sounds pretty good. I start reading the KRM developer documentation and stumble on the Security section:

The ZonicKRM submits orders through an SSL connection for security, however pricing information is currently passed from the application to the ZonicKRM as an XML string.

If this data is not checksummed, or otherwise protected, a malicious user may be able to edit the XML string within an application's executable and submit an order with an invalid price.

In the long term, this attack will be denied by moving the responsibility for pricing information from the KRM library to the KRM server. When this process is complete, vendors will be able to override the pricing information in shipping copies of an application using their Kagi database entry.


WHAT THE FUCK ? The user is able to choose the price he wants to pay ? Can't be true, this part of the documentation must be outdated. Guess what... it's not, long term is long term!

Note that checksumming or protecting is pure bullshit as long as the price comes from the application and not Kagi's server.

I searched for the first shareware using KRM I found, opened it with an hex editor, did search and replace of the string 30.00 to 01.00 and I indeed successfully ordered the shareware for $1.

This is totally irresponsible from Kagi. I don't know how this registration scheme could have been designed this way in the first place. No sensible person can design an ordering system where the price is set by the client.

Please don't flame me, I'm a good guy. I contacted the $30 shareware author and offered to pay the remaining $29 I owe him. I should have searched a bit longer for a cheaper shareware. $1 + $29 is a bit expensive to demonstrate that KRM sucks ;-)

The Bottom Line
Don't use KRM as long as you can't set the price of your shareware on Kagi's server.

Wednesday, September 03, 2008

QuietXcode

Have you ever noticed the Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x319a380, has non-zero refcount = 1 messages in the console ? Chris Espinosa replied about these warnings to Matt Neuburg:


The second one is an error deep in some system framework when running under Garbage Collection that we have not been able to track down yet. It simply means that somebody has neglected to do a final release on a memory block that nobody has kept a pointer to (making the final release technically impossible). The Garbage Collector knows the block is inaccessible and is freeing it, but is warning us that somebody forgot to formally release it before the pointer to it went out of scope. Bad form, but no actual harm.

I think it actually harms. These messages are filling the console so much it becomes unusable. I'm a big fan of GeekTool and I always have the tail of the console on the desktop. Now, it looks always the same and interesting messages from various applications are lost in the mass of free_garbage messages:

Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x319a380, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x31bf6e0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3263da0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3274eb0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x319a380, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x31bf6e0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3263da0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3274eb0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x201c8b0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3256110, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x201c8b0, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3256110, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x2045540, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x208ca10, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x2045540, has non-zero refcount = 1
Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x208ca10, has non-zero refcount = 1

So I decided to tackle the problem and here is my Solution: QuietXcode 1.1.4 (5 KB). This is an Xcode plugin that patches the culpable call to malloc_printf("free_garbage: garbage ptr = %p, has non-zero refcount = %d", ...).

You can build the plugin either by typing xcodebuild in a terminal or by building it (⌘ + B) in Xcode. Building the plugin will also automatically install it into your ~/Library/Application Support/Developer/Shared/Xcode/Plug-ins folder.

Once it's installed, you have to relaunch Xcode. You should see the message <QuietXcode> loaded successfully in the console and no more free_garbage messages.

The plugin performs a safe patch, that is, if it does not find the expected call to malloc_printf and the expected Xcode version (greater than or equal to 3.1/1099), it logs a more or less comprehensive error to the console. Have fun browsing the source code, it demonstrates how to use the dyld and mach-o apis to locate non exported symbols and the mach api to dynamically patch code.

Note that the plugin is for i386 only, porting it to ppc and/or 64 bits is left as an exercise to the reader.

Friday, August 22, 2008

Exploring iPhone OS 2 files

Update: This technique also works with iPhone OS 3.x. You will find the VFDecrypt keys on The iPhone Wiki Firmware page. Just select the appropriate iPhone model and Version/Build of your firmware.

It turns out to be pretty simple:


  1. Download iPhone OS 2.0.2 (5C1)
    curl -O http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-5241.20080818.t5Fv3/iPhone1,2_2.0.2_5C1_Restore.ipsw

  2. Unzip the ipsw which is actually a zip file
    unzip iPhone1,2_2.0.2_5C1_Restore.ipsw

  3. Download vfdecrypt
    svn co http://iphone-elite.googlecode.com/svn/trunk iphone-elite

  4. Compile vfdecrypt
    make -C iphone-elite/vfdecrypt_win32

  5. Decrypt the dmg (key from The iPhone Wiki)
    ./iphone-elite/vfdecrypt_win32/vfdecrypt -i 018-3978-1.dmg -k 31e3ff09ff046d5237187346ee893015354d2135e3f0f39480be63dd2a18444961c2da5d -o iPhoneOS-2.0.2.dmg

  6. Mount iPhone OS dmg and start exploring
    open iPhoneOS-2.0.2.dmg

Do not buy iQuarantine X

From iQuarantine X website:


  • iQuarantine X is not a background script or a script that gets attached to files or folders.
  • iQuarantine X is the first application to make the LEOPARD FILE QUARANTINE ALERTS go away.
  • iQuarantine X is the easiest way to rid LEOPARD of all FILE QUARANTINE ALERTS.

So, if it's not a script, what is it (beside a scam) ?
It's a hack that binary patches a system framework (/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore) by short-circuiting the private function _FSAllocateQuarantineData.

So we have four good reasons not to buy it:
  1. It has an unacceptable upgrade policy.
  2. It binary patches a system framework.
  3. Developer does not reply to e-mails.
  4. You can disable the Leopard quarantine for free with an official technique.

Thursday, July 10, 2008

The missing NSLocale documentation

+ (NSArray *)preferredLanguages
Return Value
The user's language preference order as an array of NSString objects, each of which is a canonicalized IETF BCP 47 language identifier. This is defined by the user in System Preferences → International → Language.

For your information: this is stored in the gloal domain in the AppleLanguages key.

+ (id)currentLocale
Return Value
The logical locale for the current user. The locale is formed from the settings for the current user’s chosen system locale overlaid with any custom settings the user has specified in System Preferences → International → Formats.
This method may return a retained cached object.


+ (id)autoupdatingCurrentLocale
This one I have not understood how it is supposed to be used. If someone knows, please let us know in the comments.

Now, if what you want is the current language, you should not use either of these methods. Instead you should use something like NSLocalizedString(@"ISOLanguageCode", @"iso language code"). Then you have to define "ISOLanguageCode" = "en"; etc. in all your Localizable.strings files.

Tuesday, June 10, 2008

Mac OS X bug on non english systems

Mac OS X 10.5 (Japanese): Disk Utility "internal error" alert with 7-pass erase or 35-pass erase

Symptoms
In the Japanese language version of Mac OS X 10.5, when performing a Secure Erase (7-pass or 35-pass erase) in Disk Utility, this alert may appear: "Disk Utility internal error. Disk Utility has lost its connection with Disk Management Tool."

Resolution
Change the Mac OS X language version to English before performing the secure erase.

I thought this kind of problem was Mac OS 9/Classic history.

Thursday, February 14, 2008

Upgrading a System Preference pane

The System Preferences application provides a convenient way to install a preference pane. Double-clicking the preference pane will prompt the user if he wants to install it for the current user only or for all users of the computer. Then System Preferences will copy the preference pane to ~/Library/PreferencePanes or /Library/PreferencePanes according to what was chosen asking for administrator password if necessary. Finally the preference pane will be loaded and presented to the user.

Now, let's see what happens when a preference pane is upgraded. Again, System Preferences is smart: it is able to detect if an older version of the same preference pane is installed and proposes to replace it [1]. Everything seems alright, but it is actually not! Things are more complicated if the preference pane to upgrade has already been loaded. That is, if the user already clicked the preference pane.

Indeed, preference panes are just a special kind of bundle which is loaded into System Preferences with the -(BOOL)[NSBundle load] method (cf. -(BOOL)[NSPrefPaneBundle instantiatePrefPaneObject] method of the PreferencePanes framework). The problem is that on Tiger, a NSBundle can not be unloaded. So when upgrading an opened preference pane, the old code is not unloaded and as a consequence the new code is not loaded. This is because System Preferences calls the -(BOOL)[NSBundle load] method which returns YES, meaning that the bundle was successfully loaded or that the code has already been loaded. In the case of an already opened preference pane, that's how the result of the load method should have been interpreted. Unfortunately, it is interpreted as if the bundle was successfully loaded and System Preferences thinks it has loaded the new bundle, but it has not.

This is very problematic because at this point, the resources (nib files, pictures etc.) of the new bundle have already been copied. So we have the old code which is accessing the new resources. I let you imagine the numerous problems this situation can cause. At best, exceptions will raise and your preference pane will be half working. At worst, your preference pane will simply crash.

So, how do we fix this problem?

First, the preference pane must detect itself when it's upgrading over an older already loaded version as System Preferences does not detect it [2]. This must be done as early as possible, i.e. at the very beginning of the - (id)initWithBundle:(NSBundle *) bundle method. It is possible to detect this situation with the help of the version of your NSPreferencePane subclass. See my detection snippet to understand how detection works.
Once this is detected, we must properly reload the new preference pane. This must be achieved by quitting System Preferences and relaunching System Preferences. This is the not that elegant solution to unload the old preference pane. The elegant solution would be to unload the bundle. This is left as an exercise to the Apple engineers for a future version of System Preferences.

Relaunching System Preferences and selecting the preference pane is quite tricky. A second executable must be responsible for relaunching the System Preferences application. Also, it is nicer for the user if the preference pane he just upgraded is automatically selected. Automatic selection of the pref pane is achieved through Apple Script. Please refer to my reload snippet for implementation details. Note that once you have compiled the reload executable, you have to place it inside the resources directory of your preference pane. Do not place it inside the executable directory (Contents/MacOS) if you do not want to see the reload application popping up in the Dock.

With this reload code in place, if the user ever happens to upgrade a preference pane while the older one was loaded, he will experience a System Preferences flicker as it will quit and reopen right away. While this might be surprising to him, this is still better than a half working preference pane or a crash.

If anything is unclear, just say it so in the comments and I will try to elaborate. If everything is clear, just pick up my code snippets and implement them in your preference pane as soon as possible ;-)



1. System Preferences uses the CFBundleGetVersionNumber function to retrieve the version numbers of the new and old bundles as an UInt32 in order to compare them. The documentation says If the bundle’s version number is a number, it is interpreted as the unsigned long integer format defined by the vers resource on Mac OS 9. What every developer understands is that if your Info.plist CFBundleVersion key represents a number (e.g. "519"), the value returned by CFBundleGetVersionNumber will be 519. This is not what actually happens. If you want CFBundleGetVersionNumber to return 519, you have to add an undocumented key in your Info.plist file: CFBundleNumericVersion. Make sure you define it as <integer>519</integer> and not <string>519</string>.

2. Note that on Leopard, this situation is actually detected and a dialog is presented to the user telling he must quit System Preferences and then open it again. Unfortunately, no automatic action is taken to circumvent this annoying behavior. System Preferences could restart itself or unload the bundle (this is possible since Leopard) but as of Mac OS X 10.5.2, none of this action is performed.

Friday, January 25, 2008

QuickTime 7.4 and Perian subtitles fix

With QuickTime 7.4, subtitles automatically added by Perian have stopped working. In order to get them back, download and install Front Row Trailers, go to the QuickTime Components tab, and install Perian 1.0.0.2. If the proposed version is below 1.0.0.2, hold the alt (option) key while clicking the Refresh button.

For thoses wondering, this is an unofficial build of Perian 1.0 onto which two patches have been applied. Note that the future Perian 1.1 release will also be able to read subtitles.

Enjoy, QuickTime 7.4 can read subtitles again, no need to downgrade to version 7.3.

UPDATE: Perian 1.1 is now released and has addressed the problem. Note that subtitles still do not work in Front Row on Leopard.

keywords: QuickTime 7.4 Perian subtitles srt