Malware Analysis Series

Deep technical malware analysis reports, reverse engineering tools, unpacked stages, and recreated TTPs with detections for infamous malware families.


Project maintained by shaddy43 Hosted on GitHub Pages — Theme by mattgraham

Cracked Haven — Why You Should Avoid Cracked Software

Cracked software reverse engineering case study

Let’s be honest: most of us have used pirated software at some point. The appeal is obvious — an expensive product, free. The risk is much less obvious, and that is exactly the problem. A cracked binary has, by definition, been modified by a stranger. Once someone is already patching machine code inside a program you have chosen to install and run with your own privileges, there is nothing technical stopping them from patching in something else as well.

This post does two things. First, the defensive half: how to use binary patch diffing to verify what a crack actually changed — a genuinely useful skill for anyone who needs to establish whether a binary has been tampered with. Second, the demonstration: taking the clean binary and showing how little effort it takes to hide arbitrary code inside it, so the risk stops being abstract.

The subject is a cracked Adobe Photoshop CS6 obtained from a piracy site — one I had personally used for years before moving into security, and which I decided to go back and audit.

Prerequisites: this write-up assumes background in reverse engineering, x86-64 assembly, Windows APIs, disassemblers such as IDA, and basic binary patching / patch diffing.

⚠️ Disclaimer: this is for educational purposes only. I do not support piracy in any form. The goal is awareness — showing why cracked software is unsafe and how its integrity can be checked. Anything you do is your own responsibility.

⚠️ Note: This is a re-write by AI, but have been vetted by Author.

📄 The original report is also available as a PDF. The demonstration binary and supporting files are in the GitHub repository.


Key takeaways

# Takeaway
1 A crack is an unaudited binary patch written by an anonymous author, running with your privileges. Trust is entirely misplaced by default.
2 Binary patch diffing can verify it. Comparing the official and cracked binaries with BinDiff reveals exactly which functions changed — and therefore whether anything beyond the licence bypass was added.
3 This particular crack was clean — only two functions differed, both purely licence-check bypasses. Reassuring, but that was luck, not a guarantee.
4 Adding hidden code is trivial. Using only IDA Freeware and a hex editor, arbitrary API calls can be patched into the same binary.
5 The demo used MessageBox; it could just as easily have been ShellExecute or WinExec — downloading and running a payload every time the application launches.
6 Verification is the only defence if you insist on running untrusted binaries — and the real answer is to use legitimate software.

Part 1 — Verifying the crack: binary patch diffing

In Photoshop CS6, amtlib.dll is the library responsible for licence validation. Cracking Photoshop amounts to replacing that one library. The question worth asking is: what else did the replacement change?

The approach comes straight from vulnerability research — the same root-cause-analysis workflow used to compare a patched and unpatched binary to locate a fix. Here the comparison is between the official amtlib.dll and the cracked amtlib.dll, using IDA with the BinDiff plugin by zynamics. (IDA Pro is expensive; IDA Freeware is sufficient for all of this, and is what was used.)

The result is reassuringly small — only two functions differ:

BinDiff showing only two changed functions between the official and cracked amtlib.dll

That immediately bounds the problem. Whatever this crack does, it does it in two places, and both can be read in full.

Changed function 1 — always return 1

Side-by-side comparison of the first changed function

The official version shows a control flow graph with a series of blocks executing after various condition checks — the actual licence validation logic. The cracked version replaces all of it with a trivial stub that always returns 1. No licences are validated.

Changed function 2 — always return 0

Side-by-side comparison of the second changed function

Similarly, the second function always returns 0 without checking anything. This looks like a pre-condition gate: the first function only runs its validation path if this one returns 0, so the crack forces that branch.

The verdict

This crack was clean. Two functions, both pure licence bypasses, no additional code. The software I had been using for years had not been backdoored.

But note what that conclusion rests on: I had to check. Nothing about downloading the file told me that. A different crack, from a different uploader, could have contained anything — and binary patch diffing is how you would find out. That is the transferable skill here.


Part 2 — Demonstrating the risk

Knowing exactly which two functions the crack modifies, the next step is to show how easily a cracker could have gone further. I took the official, benign binary, applied the same crack — and added something extra.

The two functions to patch:

AMTPreValidateProductLicense
sub_180088520

Patching the simple one

sub_180088520 just needs to return 0. IDA Freeware’s built-in assembler is limited (it is restricted to IBM PC instructions, and better patching tools exist), but for this it is enough:

IDA assembler instruction limitation

The return value always lives in rax (or eax for 32-bit). Assembling xor eax, eax sets it to zero — anything XORed with itself is zero — and ret hands control back:

Patching the second function in IDA

Going further: injecting an API call

The first function could be finished just as quickly — move 1 into eax and return. Instead, this is where the demonstration begins. Reviewing the binary’s imports showed a MessageBox import already present, so no new import table entry was even needed.

Calling an existing import by patching requires:

  1. Knowing the address of the API import
  2. Calculating the offset for the call
  3. Pushing all parameters onto the stack frame
  4. Keeping pushes and pops balanced so the stack frame is not corrupted
  5. Calculating offsets for string parameters
  6. Computing opcodes, since bytes must be edited directly

For step 6 there is a shortcut instead of hand-assembling: write the equivalent code in Visual Studio, disassemble it, and read the opcodes off the debugger (for anything complex, MASM works too):

Opcodes generated by the Visual Studio debugger

IDA’s live mapping between instructions and hex makes applying them straightforward — click an instruction in IDA View, switch to Hex View, and the corresponding bytes are highlighted ready to edit:

Patching instruction bytes in IDA's hex view

Creating string parameters

MessageBox needs strings. Since there is no compiler to allocate them, they go into unused buffer space in the data section — find empty bytes, edit them to the desired string, and define them as string literals (ALT + A in IDA).

Creating string variables in the data section

Two variables were created: the message body and the caption. One important detail — MessageBoxW takes wide strings, so the variables must be defined as UTF-16 rather than 8-bit ASCII.

Calculating offsets

Loading those addresses into the parameter registers needs relative offsets, which follow a simple formula:

offset = callee_address - caller_address - instruction_size

Worked through for the message text:

Value  
callee_address (string) 180228EED
caller_address (instruction) 18009DAD9
instruction_size 7
offset 18B40D

From the Visual Studio opcodes, lea begins 48 8D, 15 selects the destination register, and the remainder is the offset in little-endian. The complete instruction becomes:

lea rdx, offset:text   →   48 8D 15 0D B4 18 00

Instructions loading offset values into registers

Opcodes for the lea instructions

The remaining parameter is 0 — another xor — and the final step is the call itself. Same method: compute the offset (0F8AF0), combine with the call opcode FF 15:

Patched instruction calling the Windows API import

Every other instruction in the function was replaced with NOP, and the crack itself — return 1 — appended. Applying the patch from IDA’s edit menu and dropping the result in place of the official binary completes it.

The result

Backdoored Photoshop displaying a message box on launch

Photoshop now pops a message box on every launch: “Beware, You have been hacked !!!”

That message box is harmless. That is the entire point — it is a placeholder. The same handful of patched instructions could have called ShellExecute, WinExec, or any process-creation API. A single parameter change turns the demonstration into a downloader that fetches and runs a payload every time the application starts, with no visible indication to the user.

A short capture of the running result is in the repository as Assets/Cracked.gif.


What this means in practice

For individuals:

For defenders:

The honest conclusion: verification is a mitigation, not a solution. Most users cannot reverse engineer a DLL, and even a clean crack today can be replaced by a backdoored one in the next release. Use legitimate software from the vendor.


Tools used


Repository contents

The recreated demonstration binary is in tools_and_scripts, archived with the password in the same folder. To reproduce the demonstration, rename it to amtlib.dll and place it in a Photoshop CS6 directory in an isolated test VM.

⚠️ This binary deliberately executes injected code on launch. It is an awareness demonstration, not a tool — run it only in a disposable VM, never on a machine you care about. See SECURITY.md.


Disclaimer

The intended purpose of this exercise is solely to spread awareness so people avoid cracked software — because nothing is free. If something is free, then you are the product.

Artifacts in this repository must be used for educational purposes only.

← Back to all analyses