Thwarting Piracy: Anti-debugging Using
GPU-assisted Self-healing Codes
Adhokshaj Mishra
Uptycs India Pvt. Ltd.
Bengaluru, India
me@adhokshajmishraonline.in
Manjesh K. Hanawal
MLiONS Lab, IEOR, IIT Bombay
Mumbai, India
mhanawal@iitb.ac.in
Abstract—Software piracy is one of the concerns in the IT
sector. Pirates leverage the debugger tools to reverse engineer the
logic that verifies the license keys or bypass the entire verification
process. Anti-debugging techniques are used to defeat piracy
using self-healing codes. However, anti-debugging methods can
be defeated when the licensing protections are limited to CPU-
based implementation by writing custom codes to deactivate the
anti-debugging methods. In the paper, we demonstrate how GPU
implementation can prevent pirates from deactivating the anti-
debugging methods by using the limitations of debugging on
GPU. Generally, GPUs do not support debugging directly on
the hardware, and therefore all the debugging is limited to CPU-
based emulation. Also, a process running on CPU generally does
not have any visibility on codes running on GPU, which comes as
an added benefit for our work. We provide an implementation on
GPU to show the feasibility of our method. As GPUs are getting
widespread with the raise in popularity of gaming software,
our technique provides a method to protect against piracy. Our
method thwarts any attempts to bypass the license verification
step thus offering a better anti-piracy mechanism.
Index Terms—Cyber security, Anti piracy, Anti-debugging,
GPU-assisted Self-healing
I. INTRODUCTION
Anti-debugging techniques are specially crafted chunks of
code that involve one or more methods to detect, and possibly
prevent debugging attempts on the target process. Most of
the time, these codes are integrated into the final binary blob
of the program they are trying to protect; however ”out of
process” anti-debugging is also possible by employing system-
level hooking in user mode, or kernel mode.
Since most of the debuggers rely on modification of de-
buggee process to large extent (e.g. break-points are set by
overwriting instructions, changing permissions, altering error
handler chains, etc.), anti-debugging techniques mostly rely
on detecting such modifications and possibly reverting them
to their original state. Since reverting to the original state
requires modification of our own process, we commonly see
some variation of self-modifying codes being used for such
purposes.
Self-healing codes are a special case of self-modifying
codes, which have the ability to detect any modifications in
their code and revert it to its original state before executing
it. Although these techniques can restore sufficiently large
modifications, in practice these are kept limited to only critical
parts of the code. These are often used to subvert debugging
techniques allowing the protected processes to evade from the
eyes of debugger tools (e.g. process not stopping on a break-
point).
Anti-debugging techniques generally can be grouped in the
following categories though not all techniques are applicable
to all platforms:
1. Timing and Latency Analysis: These techniques rely on
the difference in time taken to run a known calibrated code.
Debugging tools generally make the underlying process run a
bit slower due to their invasive nature.
2. Process Detection: These techniques rely on detecting the
presence of known debugging and related tools. If such tools
are running on a system, the chances of the process being
under watch is fairly high.
3. Memory Analysis: Many debugging tools tend to alter
memory maps in different ways (different parts of code and
data being loaded in slightly different locations, differences in
stack, heap, etc), which can be used to detect debugging in
such cases.
4. Break-point Detection: These techniques rely on the
fact that setting a software break-point alters machine code
in memory. The program scans its own memory to search
machine code for software break-point (e.g. 0xCC on x86 and
AMD64) in code regions.
5. Patching Detection: These techniques are one step ahead
of techniques described in (4), and these are able to detect
arbitrary patching (machine code modification) in process
memory.
6. Monitoring Debugger APIs: These techniques rely on the
fact that only one process can act as a debugger for another
given process at a time. In other variations, debugger APIs
exposed by the platform are hooked and monitored globally
to detect debugging attempts (e.g. DebugActiveProcess(...) /
WaitForDebugEvent(...) on Windows, ptrace on Linux).
7. Monitoring Exception Handlers: Many times when a
debugger is attached to a process, exceptions are trapped and
handled by the debugger without passing the exception back
to the application for continued execution. Occasionally these
exceptions can even crash or terminate a process when run
under a debugger and be handled gracefully when running
without a debugger attached. These discrepancies can be used
arXiv:2210.11047v1 [cs.CR] 20 Oct 2022