The Big Idea
This article is for educational purposes only. Note the license agreement (EULA) states that modification of the software is strictly prohibited, however, reverse engineering rights are protected by the French copyright laws. Here, we’ll attempt to reverse engineer the Hopper Disassembler and figure out their license handling technique.
Please buy a license to support the creators of Hopper.
Getting it to work first
Alright, let’s get rolling. The first step is to locate the Hopper executable, which is usually at /opt/hopper-v4/bin
.
Know that the free version of Hopper does not allow you to save files and will display
a dialogue saying: “You cannot save with the demo version.” The free version also has a session time limit of 30 minutes.
Starting up Hopper, we see the license dialogue with a button labelled “Try the Demo.”
Loading Hopper into IDA and performing a string search reveals the “Try the Demo” string at:
1 |
|
And following it leads us to:
1 |
|
Further following the XREF brings us finally to the function sub_506CD0
which is most likely responsible for showing the license dialog.
Therefore, it’s best to rename it to ShowLicenseDialog
. Simple logic deduction leads us to believe that this function is run only
when a license is not installed/registered with the software.
Setting a breakpoint in GDB and viewing the call stack reveals several functions that were called:
1 |
|
Now we just need to open every address (f1
through f7
) in IDA and see what’s there.
We finally found the right one!
Most functions on the call stack are boring, usually, Qt function calls and such. However, at 0x638031
we see a spicy one:
1 |
|
Note a peculiar pattern: a call to sub_504550
followed by a test
instruction and a jnz
past the function chain
(the chain that eventually leads to ShowLicenseDialog
). This is a strong candidate for a common coding pattern:
1 |
|
Ah, things are looking good! A closer look at sub_504550
confirms my suspicions.
1 |
|
Note the call to sub_502E70
which is a function that references the string “https://www.hopperapp.com/validate_license_v4.php"
followed by several network calls and requests. We now know that sub_504550
is a CheckLicense
function. Jackpot!
Patching the sub_504550
function to always return true (al
=1 ) is easy, we can do it like so:
1 |
|
Good Eyecandy
You may have noticed the “Demo Version” watermark present in the background of the program. Getting rid of it is as simple as searching for the string “Demo Version” in IDA:
1 |
|
A quick patch overwriting the string with all 0’s should do the trick:
1 |
|
Now, we’ll attempt to customize the license window.
We should search for the string “Demo version” or “Hopper Standard Edition,” as those should appear somewhere near the “About” window dialog code.
1 |
|
Note the following variables declared several bytes further down:
1 |
|
Again, following the XREF leads us to sub_4C0930
(which we’ll rename to PrintLicenseName
). Looking at the graph, we see:
I’ve labeled the graph this time since it’s quite messy when taken out of context. The pseudocode for this block would be:
1 |
|
The following block contains the string aPersonalLicens
, which should be the string we want to print to the screen.
1 |
|
Upon further investigation, we see that many calls branch from loc_4C0A9A
. Here, we must ensure all branches to loc_4C0D45
are valid with a direct jump (bypassing loc_4C0A9A
). When verified, we can just replace the jnz loc_4C0CD2
instruction with a plain jmp loc_4C0A9A
, thus bypassing all checks. Then we have the entire aPersonalLicens
to ourselves to customize. So, we first patch:
1 |
|
To:
1 |
|
Then override aPersonalLicens
to display a custom message. Note the message length of 29h
or 41 characters. That is quite restrictive. But also notice that aComputerLicens
(which is unused as we skipped over all logical branches leading to its XREF) immediately follows aPersonalLicens
, granting us a total of 77 characters to work with (by overwriting aComputerLicens
).