Altering specific configuration values in the On-Screen Keyboard of Windows 7

Solution 1:

Where in the code does it find our key?

Using Process Monitor, digging into the for the ETL event reading out that value stack trace gives us:

"Frame","Module","Location","Address","Path"
...
"3","ntdll.dll","NtQueryValueKey + 0xa","0x7fbce17344a","C:\Windows\SYSTEM32\ntdll.dll"
"4","KERNELBASE.dll","LocalBaseRegQueryValue + 0x15d","0x7fbcb1a3e1d","C:\Windows\system32\KERNELBASE.dll"
"5","KERNELBASE.dll","RegQueryValueExW + 0xe9","0x7fbcb1a3c19","C:\Windows\system32\KERNELBASE.dll"
"6","ADVAPI32.dll","RegQueryValueExWStub + 0x1e","0x7fbcba412fe","C:\Windows\system32\ADVAPI32.dll"
"7","osk.exe","OSKSettingsManager::GetOskSetting + 0xc7","0x7f72356057f","C:\Windows\System32\osk.exe"
"8","osk.exe","OSKSettingsManager::Initialize + 0x6e","0x7f72355ffe2","C:\Windows\System32\osk.exe"
"9","osk.exe","OSKSettingsManager::GetOSKSettingsManager + 0x64","0x7f72355fee4","C:\Windows\System32\osk.exe"
"10","osk.exe","COskNativeHWNDHost::DetermineOSKWindowSizeAndLimits + 0x5a","0x7f72355d4fa","C:\Windows\System32\osk.exe"
"11","osk.exe","COskNativeHWNDHost::Initialize + 0xaa","0x7f72355d28e","C:\Windows\System32\osk.exe"
"12","osk.exe","PresentOSK + 0x112","0x7f723557882","C:\Windows\System32\osk.exe"
"13","osk.exe","wWinMain + 0x356","0x7f723557f16","C:\Windows\System32\osk.exe"
"14","osk.exe","operator new[] + 0x37a","0x7f723564b12","C:\Windows\System32\osk.exe"
"15","KERNEL32.DLL","BaseThreadInitThunk + 0x1a","0x7fbcd24298e","C:\Windows\system32\KERNEL32.DLL"
"16","ntdll.dll","RtlUserThreadStart + 0x1d","0x7fbce19e229","C:\Windows\SYSTEM32\ntdll.dll"

We can see that OSKSettingsManager::GetOskSetting reads out the value.

So, what does that portion look like? Can we debug it?

Looking into that function with WinDBG, it accesses that registry key right before 000007f7 23560517.

osk!OSKSettingsManager::GetOskSetting:
...
000007f7`2356050e ff15440bfeff    call    qword ptr [osk!_imp_RegOpenKeyExW (000007f7`23541058)]
000007f7`23560514 448bd8          mov     r11d,eax
000007f7`23560517 85c0            test    eax,eax
000007f7`23560519 751f            jne     osk!OSKSettingsManager::GetOskSetting+0x82 (000007f7`2356053a)
000007f7`2356051b 488b0b          mov     rcx,qword ptr [rbx]
...

Now, the problem here is that when I try to breakpoint at that location I can no longer type anything because osk.exe adds itself to the input drivers. This can easily be seen by holding a modifier key like Alt on your keyboard, this lights it up in osk.exe.

Looking through the code for additions or subtractions, I only see something happen with 40 hexadecimal which is 64 decimal. So that's also nothing related to the number.

It might be in one of the four cmp (compare) instructions, but that would require debugging information. Or it could happen in a higher up function altogether, which would require more investigation. But without the ability to debug it without losing input capabilities, this is a very hard thing to do...

Seems that finding the right location will require a debug cable as the computer on which you debug loses its input capabilities, or is way too slow due to the overhead of debugging. As I don't currently have a laptop with a 1943 port, I'm unable to do this debugging myself. It would be able to do this, and yes, it would literally freeze your OS. Debugging an OS instead of an application is fun... ^^

Wait, we have access to the symbols! Can we find the offending code?

OSKSettingsManager::ClearTransferKey(void)
OSKSettingsManager::GetOSKSettingsManager(OSKSettingsManager * *)
OSKSettingsManager::GetOskSetting(ulong,ulong *)
OSKSettingsManager::GetOskSetting(ulong,ulong *,int)
OSKSettingsManager::Initialize(void)
OSKSettingsManager::NotifyListeners(ulong,ulong)
OSKSettingsManager::RegisterListener(void (*)(ulong,ulong))
OSKSettingsManager::SQMStartupSettings(void)
OSKSettingsManager::SetOskSetting(ulong,ulong)
OSKSettingsManager::SetOskSetting(ulong,ulong,int)
OSKSettingsManager::_HandleUpdateAllListeners(void)
OSKSettingsManager::_KeepSettingValueInBounds(ulong,ulong *,int)
OSKSettingsManager::`scalar deleting destructor'(uint)

Taking a closer look, you'll notice the offending function:

OSKSettingsManager::_KeepSettingValueInBounds(ulong,ulong *,int)

If we walk through that function we first see:

mov     edi, edi
push    ebp
mov     ebp, esp
mov     eax, [ebp+arg_4]
imul    eax, 14h
cmp     dword_4B7598[eax], 0
jz      short loc_41BC36        

Okay, this compares something and then jumps to another location. What's there?

pop     ebp
retn    8

So, if the condition decides that it must jump it will just leave the function and not change anything.

So, how do we make it always leave the function?

Change the jz instruction into a jmp instruction which always do the jump, you can find it at relative offset 41BC10. In case your program calculates offsets different, you need to know that it uses 401000 as a basis so subtracting gives us the absolute offset 1AC10.

Please note that changing 74 (JZ) in the hex editor to E9 (JMP) won't work. You can't do this in a hex editor, you will need something that disassembles and reassembles the code but that's not necessarily easy to find (eg. IDA Professional which people actually pay for, can't produce proper c code or an executable. OllyDBG, commonly used in the patch community, can't even open the executable.). And then even, Microsoft might be protecting its executable against tampering, because this could be considered against the EULA; so, good luck!

Meh! This is hard, I just want to type fast using my mouse / eyes / ...

You should definitely check out Dasher which is way faster than an On-Screen Keyboard. It simply works by moving your mouse towards letters; horizontal movement determines the speed and vertical movement select the letters. With a dictionary built in it can even size the most likely letters to be bigger, it also tries to learn from your movement such that the speed and letters are really accustomed to your usage.

An image speaks more than a thousand words...

Of course this is rather small and not really fast as it's an example, but you could resize it to be at the right side of your screen such that it does not interfere with your screen. This allows you to type as fast as you can...

Here is a good example of how the predictions allow you to type any language more quickly:

Also note that the letters at the right are sorted in a specific order, such that the major direction (up, mid or down) chooses between the different types (lowercase, uppercase, numbers and punctuation); and then within such major direction, your minor direction will choose between A-Z, a-z, 0-9 and so on. I've used this in the past and was actually amazed by how fluent this is compared to other competitors...

Also note that Dasher has some configuration, so you might be able to adjust something you don't like.

Solution 2:

I am afraid that most decompilers will not produce a good enough result that can be recompiled.

The decompilation can only help to pinpoint the area that HKEY_CURRENT_USER\Software\Microsoft\Osk\HoverPeriod is used, so as to find out where this value is read and where the 500 ms limit is enforced.

Once located, you should patch the binary code so as to disable the test and enforce HoverPeriod to be smaller. Patching the binary is much more feasible than recompiling.

You might need to repeat the effort if osk.exe is ever replaced by Windows Update (which I don't really expect to happen).

[EDIT]

To help you more on the way, here are some words on utilities and their usage.

You could either use a C-decompiler or a disassembler. The later type may be more useful, although it requires some small knowledge of the Intel instruction set. You will also need a good hex editor, and there are some that advertise disassembler capabilities (see link below).

project fenris advertises itself as "suite of tools suitable for code analysis, debugging, protocol analysis, reverse engineering, forensics, diagnostics, security audits, vulnerability research and many other purposes". Sounds good and recommended by many, but I have no experience with it.

REC Studio Decompiler attempts to produce a C-like representation of the code and data used to build the executable file.

Boomerang is a general, open source, retargetable decompiler of machine code programs.

Many more such utilities can be found by googling, or in :

wikibooks x86 Disassembly/Analysis Tools
wikibooks x86 Disassembly/Disassemblers and Decompilers
Open Directory : Disassemblers

A preliminary step is to disassemble osk. The resulting listing might be voluminous and require a good text-editor (normally notepad++ is enough).

Search for the string "HoverPeriod" (case insensitive). If you are lucky, the disassembler has identified it as a string and you can find it as-is. If not, you will need to search for it byte-by-byte. As a Unicode string it will look like H,0,o,0,v,0... where the letters should be replaced by their numerical codes.

Once you have found the "HoverPeriod" string, the disassembler should have placed a label somewhere before it. Use the name of this generated label to search for where it is used, to identify where it is retrieved from the registry and in which global variable is the result stored.

Once you have located the variable that holds the count, search for where it is used. What you will do depends on the format of the code that you will find. You can then decide on the patch you want.

For example, you may want to use the hex editor to replace in osk.exe a command such as :

move AX,<HoverPeriod milliseconds count variable>

into

mov AX,200       (your count of 200 milliseconds)

Be careful with differing command-lengths, where if the new command is shorter it will need to be padded-out with one-byte NOP instructions (no-operation) to the length of the old instruction.

Locating the command to patch in osk.exe may require searching if the offsets that the disassembler gives are in the code rather than in the exe file. If the hex editor doesn't support assembler search, do the disassembly while listing the original instruction bytes so you will know for which binary bytes to search for. Avoid searching for instructions that contain addresses, since addresses may be relocated in the exe, so rather search for a sequence of bytes near-to such an instruction.

You can create the new patch bytes either directly in the hex editor if it supports assembler. Or if it doesn't and you don't feel comfortable with Intel machine code, find an assembler to compile your new code and use its listing to get the compiled bytes to use for patching.

Solution 3:

May I suggest using AutoIt to automate the clicking?

The idea would be to monitor mouse idling when the cursor is within the on-screen window (e.g. in a control loop, checking at a regular interval whether cursor has changed position) and auto-click (call function SendClick) after, say, 100ms of inactivity.

Beats reverse engineering an .exe.