The Big Idea
This article is for educational purposes only. Here, we’ll attempt to reverse engineer the Hopper Disassembler and figure out their license handling technique. I’m currently running Ubuntu 18.04 with Hopper v4.5.11 installed.
Steps to Success
Getting it to work first
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 dialog 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 dialog 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
. From now on, ShowLicenseDialog
will refer to sub_506CD0
. 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 open every address (f1 through f7) in IDA and see what’s there.
Most functions are boring, usually Qt function calls and such. However, at 0x638031
we see:
1 |
|
Note a peculiar pattern: a call to sub_504550
followed by a test
instruction and a jnz
past the function chain (that eventually leads to ShowLicenseDialog
). This is a strong candidate for a common coding pattern:
1 |
|
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 like a CheckLicense
function.
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 horrendous “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.
Therefore, 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 was renamed to PrintLicenseName
). Looking at the graph, we see:
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
).
Note that after modifying aPersonalLicens
, you must change the value stored into esi
at 0x4C0D54
to match the message length of your new string. Good luck!
Results
The resultant patched bytes are (copied directly from IDA):
1 |
|
Starting up Hopper, we see that the license dialog is missing and we are allowed to save files unimpeded; therefore confirming a sucessful
patch!