Wednesday, July 8, 2009

Text input on 3.0, Part 3 -- Hooking

I think the best solution for now is put a hook at -[UIKeyboardLayoutStar setKeyboardName:appearance:]. If we see a custom name, we could put our own keyboard into the cache dictionary.

Who called -[UIKeyboardLayoutStar setKeyboardName:appearance:]? From the backtrace,
#1  0x30b16dae in -[UIKeyboardLayoutStar showKeyboardType:appearance:orientation:] ()
#2 0x30a1da78 in -[UIKeyboardImpl updateLayout] ()
#3 0x30a1a175 in -[UIKeyboardImpl setDelegate:force:] ()
#4 0x30a1815a in -[UIKeyboardImpl setDelegate:] ()

and the calling code of -[UIKeyboardLayoutStar showKeyboardType:appearance:orientation:] is
[self setKeyboardName:UIKeyboardGetKBStarKeyboardName(
orientation, keyboardType)

and UIKeyboardGetKBStarKeyboardName is hard-coded to generate the expected keyboard name. But before we start we must ensure the input mode can be handled by UIKit! In 3.0 there's a function UIKeyboardGetSupportedInputModes() which returns the 46 hard-coded input mode names. Well, we could of course use MSHookFunction to replace it, but I'd like to see if it's possible to do it with Objective-C only, because MSHookFunction is not available on x86. And actually there is a trick — we see that UIKeyboardGetSupportedInputModes() returns a shared object, which is an NSMutableArray*. What does this mean? Well, we can call this function once on initialization, modify it, and from that point on UIKeyboardGetSupportedInputModes() will record our change.

Then we need to trick UIKit into thinking our keyboard uses UIKeyboardLayoutStar. This information is extracted in UIKeyboardInputModeUsesKBStar(), which is again hard-coded. However, this function uses an NSDictionary* which cannot be accessed outside. So we can't test on simulator? Not really, we could actually hook -[NSDictionary initWithObjectsAndKeys:] instead, and swap it back to the original method when done.

There are a few other hard-coded dictionary in UIKeyboardInputManagerClassForInputMode(), UIKeyboardLayoutDefaultTypeForInputModeIsASCIICapable(), UIKeyboardGetKBStarKeyboardName(), etc. After all these are replaced we can get a totally empty keyboard without error — a good start.

Now we can insert the actual hook to -setKeyboardName:appearance: and set the keyboard. This part is not difficult because there's are reference implementations, like that disassembly of getKeyboardiPhonePortraitQWERTY() and dump of various keyboard files.

1 comment:

  1. As always, amazing, beautiful, simple insights ! Keep up the amazing work.