Disabling updates in MacsFanControl (by replacing a byte)
Wed, Jun 14, 2017MacsFanControl is this utility that enables manual controls for my Macbook’s fans on Windows. Recently, I had the unpleasant chance to interact with their update mechanism.
The update prompt must be acknowledged, and there is user-configurable option to disable or ignore the updates. Considering the app works just fine as it is, along with the fact that future updates will still have this issue, I chose to disable the updater permanently.
Digging into MacsFanControl.exe
- Unprotected Win32 PE (yay)
- Single binary referencing several generic libraries
- Uses Qt
Loading the binary in IDA and checking Strings yields promising results:
The nag string was right there, with only one reference to it.
Next up was finding the region where MacsFanControl decides to nag; by travelling “up” the cross references, there should eventually be a point where the decision-making instructions appear.
Skipping the irrelevant instructions was especially smooth due to IDA’s ability to identify and indicate Qt functions.
About 6 x-refs later, this jump-table appears, with the nag path at 0x436770
as one of the options Case 5
(top center). Interestingly, there are 2 paths where the nag path can take:
- Continue executing and therefore arrive at the default case (bottom left)
- Jump to
0x43679A
, which then arrives at0x43679F
(bottom right)
0x43679F
also happens to be Case 0
of the jump table.
The actions of the 2 branches can be understood from QDialog::exec()
. Based on the nag dialog’s result, the application decides whether to
- Exit. It “does nothing” and ends up at the default case.
- Continue to start MacsFanControl, at
0x43679F
orCase 0
. This is whereQApplication::exec()
is called, and also where we’d like to go.
Decisions at the jumptable
The jumptable above, which decides whether the application has to nag, works by..
- First having the zero-indexed result (which case?) stored in
esi
- Checking if
esi
is greater than 5, and if so, jumps to the default case at0x436769
- Determines the address to jump to: The
jmp
instruction jumps to the address stored in0x436840 + (esi * 4)
The memory region at 0x436840
contains 6 32 bit addresses (4 byte groups) in little-endian (expressed in reverse).
To reach the nag case, the value of esi
has to be 5
. The jmp
instruction will read the value at 0x436854
calculated from 0x436840 + (5 * 4)
, and therefore arrive at Case 5
at address 0x436770
(70 67 43 00
in memory).
Patching away the nag
Ideally, the nag case should never be reached. One possibility is ensuring that esi
never reaches 5
, by disabling (nop)
the instructions which assigns it, or by replacing the value of esi
before the jump (mov esi, 0)
.
Another possibility, which I prefer, is to replace the addresses at the jumptable. Since Case 0
should be the way to go, replacing the address at 0x436770
(70 67 43 00
) with the values of (9F 67 43 00
) will reroute program execution to our desired destination whenever it decides to nag.
This process is incredibly simple - searching for the array of bytes 70 67 43 00
in your favourite hex editor and replacing 70
with 9F
is all it takes to patch the application. (More specifically, at 0x35C54
, replace 9F
with 70
)
Yay! Turning off updates really shouldn’t require IDA..
Why not approach this from a network perspective
MacsFanControl stores the last known latest version that it has obtained from its servers. The nag will permanently remain until the application is updated.