RE Adventures 1 : The mouse config tool
Kapil Kapre | Saturday, January 16, 2010

Up until a few years ago, I was a huge OS optimization junkie, but thankfully the level of obsession has gone down in recent years. I still remember the time when a friend had come over to exchange some data and we spent almost an hour trying to get the bloody network sharing to work in XP. As it happened I was in my 'no open ports' OCD stage and I had "fixed" Windows so that no TCP/IP ports were left open. Apparently that involved neutering netbt/netbios and other networking components to the point where getting them back was a chore. I can't remember but I think we ended up setting up an FTP server.

So, the new me, doesn't care too much about crap that runs on system startup or the terrible service-bloat that the popular malware iTunes forces on you just to copy a mp3 file to a fucking flash drive. Ofcource, I also do realize that many of the Windows optimization tips are just snake-oil and there won't be a perceptible difference in OS performance (or at least, one that justifies all the time wasted troubleshooting problems caused by your b0rked system state). Anyway, I recently moved up to Win7 from Vista and as I was installing my mouse (Razer Diamondback 3G) driver, my dormant OCD stirred from its deep slumber and messed with my head.

I realized I wasn't really using the config tool for any purpose other than to set my mouse's CPI to 800 instead of the native 1800 and that I ought to get rid of it. Ofcource as it turned out the mouse doesn't even have any internal memory to save this setting and that the mouse config tool, on system startup, sets the dpi to 800.

After poking around it seemed like the tool was functionally separated into 4 main binaries : razerhid.exe, razertra.exe, razercfg.exe, razerofa.exe. Just by looking at the naming I guessed that the razercfg.exe was the config tool itself, the razertra.exe was the system taskbar (tray) tool and the razerofa.exe was the on-the-fly sensitivity tool. I didn't quite know whatrazerhid.exe was but I just guessed it was something to do with USB HID class devices. The process hierarchy had razerhid.exe as the parent process, with razertra.exe and razerofa.exe as child processes. And razercfg.exe was the child process of razertra.exe

Now, I just had to find out where the settings were stored, and how specific commands were sent to the mouse to change the CPI. The registry was the first guess so I ran a registry monitor to get the location of the settings. So, now I knew that the settings were stored at HKCU\Control Panel\Mouse\razer\* and the one I was primarily interested in was called Dpi1600Enable. I now needed to find out which of these binaries was actually sending the command to the device driver. I fired up IDA and went rummaging through the imports table and possible LoadLibrary/GetProcAdress calls in razercfg.exe and razerhid.exe to see if I could find anything of interest. For some reason trying to debug and trace through razerhid.exe caused random bluescreens with the mouse driver. I didn't want to spend time tracking that down, so I slapped on KD and hooked up my laptop through 1394 and used a mixture of static analysis with IDA and live debugging with KD to get an idea of what was happening.

After some more poking around, one thing seemed fairly certain. razercfg.exe wasn't responsible for actually programming the mouse's driver and only seemed to deal with registry keys. I now concentrated all my efforts on razerhid.exe as it had the delicious kernel32!DeviceIoControl import that was probably doing all the dirty work of programming the mouse. After considerable debugging and tracing through the god-awful MFC disassembly I hit upon a function that was possibly performing the actual device programming.


; comments describe the function that the call points to.
.text:004031B0 sub_4031B0      proc near               
.text:004031B0                                         
.text:004031B0                 push    esi
.text:004031B1                 mov     esi, ecx
.text:004031B3                 call    sub_402DA0   ; 4 DeviceIoControl calls that use contents of the 
                                                      sensitivity settings from the registry
.text:004031B8                 mov     ecx, esi
.text:004031BA                 call    sub_402D30   ; a couple of calls to DeviceIoControl
.text:004031BF                 mov     ecx, esi
.text:004031C1                 call    sub_402EC0   ; call to SetDoubleClickTime
.text:004031C6                 mov     ecx, esi
.text:004031C8                 call    sub_402ED0   ; call to DeviceIoControl, SystemParametersInfoA, 
                                                      SwapMouseButton
.text:004031CD                 mov     ecx, esi
.text:004031CF                 call    sub_402F70   ; calls to SystemParametersInfo and RegQueryValueEx
.text:004031D4                 mov     ecx, esi
.text:004031D6                 call    sub_403110   ; calls to SystemParametersInfo
.text:004031DB                 pop     esi
.text:004031DC                 retn
.text:004031DC sub_4031B0      endp

After some more boring debugging and live execution manipulation, it seemed like sub_402D30 was the most likely candidate. Time to test it out by writing a simple program to duplicate the function sub_402D30.


; comments are IDA generated
.text:00402D30 sub_402D30      proc near               ; CODE XREF: sub_4031B0+A
.text:00402D30                 mov     eax, hDevice
.text:00402D35                 push    ebx
.text:00402D36                 push    edi
.text:00402D37                 mov     edi, ecx
.text:00402D39                 test    eax, eax
.text:00402D3B                 jz      short loc_402D97
.text:00402D3D                 mov     ecx, [edi+0E20h]
.text:00402D43                 xor     ebx, ebx
.text:00402D45                 test    ecx, ecx
.text:00402D47                 jle     short loc_402D97
.text:00402D49                 push    ebp
.text:00402D4A                 mov     ebp, ds:Sleep
.text:00402D50                 push    esi
.text:00402D51                 mov     esi, ds:DeviceIoControl
.text:00402D57                 jmp     short loc_402D5E
.text:00402D59 ; ---------------------------------------------------------------------------
.text:00402D59
.text:00402D59 loc_402D59:                             ; CODE XREF: sub_402D30+63
.text:00402D59                 mov     eax, hDevice
.text:00402D5E
.text:00402D5E loc_402D5E:                             ; CODE XREF: sub_402D30+27
.text:00402D5E                 mov     ecx, [edi+778h]
.text:00402D64                 push    0               ; lpOverlapped
.text:00402D66                 push    offset BytesReturned ; lpBytesReturned
.text:00402D6B                 push    0               ; nOutBufferSize
.text:00402D6D                 push    0               ; lpOutBuffer
.text:00402D6F                 push    0               ; nInBufferSize
.text:00402D71                 test    ecx, ecx
.text:00402D73                 push    0               ; lpInBuffer
.text:00402D75                 jz      short loc_402D7E
.text:00402D77                 push    22244Ch
.text:00402D7C                 jmp     short loc_402D83
.text:00402D7E ; ---------------------------------------------------------------------------
.text:00402D7E
.text:00402D7E loc_402D7E:                             ; CODE XREF: sub_402D30+45
.text:00402D7E                 push    222450h         ; dwIoControlCode
.text:00402D83
.text:00402D83 loc_402D83:                             ; CODE XREF: sub_402D30+4C
.text:00402D83                 push    eax             ; hDevice
.text:00402D84                 call    esi ; DeviceIoControl
.text:00402D86                 push    0Ah             ; dwMilliseconds
.text:00402D88                 call    ebp ; Sleep
.text:00402D8A                 mov     eax, [edi+0E20h]
.text:00402D90                 inc     ebx
.text:00402D91                 cmp     ebx, eax
.text:00402D93                 jl      short loc_402D59
.text:00402D95                 pop     esi
.text:00402D96                 pop     ebp
.text:00402D97
.text:00402D97 loc_402D97:                             ; CODE XREF: sub_402D30+B
.text:00402D97                                         ; sub_402D30+17 
.text:00402D97                 pop     edi
.text:00402D98                 pop     ebx
.text:00402D99                 retn
.text:00402D99 sub_402D30      endp

Bingo ! Simply sending the control code 222450h set the device to 800CPI mode and sending 22244Ch set it to 1800CPI. Finally, I can now save a combined working set of about 20 megs from occupying physical memory ! I won't necessarily notice it in my 8GB mem bucket, but hey, the OCD seems to have died off. I can sleep peacefully now.


Download:


Sample code to set DPI:
set800.cpp
set1800.cpp

Note: I've merely put these files up here for entertainment purposes. Using these might mess with razers config tool. To make this code more robust you need to add some calls to set a few registry keys, investigate what the proper practice is when using DeviceIOControl, etc (Whether you need to confirm that the device has been correctly programmed, Whether delays are needed in around DeviceIOControl calls, Whether all error conditions are handled etc)