Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
O

obfy

> 编程语言
Open source

A tiny C++ obfuscation framework

712 stars0 likes1 views
WebsiteGitHub

About

A tiny C++ obfuscation framework

Attacking the licensing problems with C++

From the early days of the commercialization of computer software, malicious programmers, also known as crackers have been continuously nettling the programmers of aforementioned software by constantly bypassing the clever licensing mechanisms they have implemented in their software, thus causing financial damages to the companies providing the software.

This trend has not changed in recent years, the more clever routines the programmers write, the more time is spent by the crackers to invalidate the newly created routines, and at the end the crackers always succeed. For the companies to be able to keep up with the constant pressure provided by the cracking community they would need to constantly change the licensing and identification algorithms, but in practice this is not a feasible way to deal with the problem.

An entire industry has evolved around software protection and licensing technologies, where renowned companies offer advanced (and expensive) solutions to tackle this problem. The protection schemes vary from using various resources such as hardware dongles, to network activation, from unique license keys to using complex encryption of personalized data, the list is long.

This article will provide a short introduction to illustrate a very simple and naive licensing algorithms' internal workings, we will show how to bypass it in an almost real life scenario, and finally present a software based approach to mitigate the real problem by hiding the license checking code in a layer of obfuscated operations generated by the C++ template metaprogramming framework which will make the life of the person wanting to crack the application a little bit harder. Certainly, if they are well determined, the code will also be cracked at some point, but at least we'll make it harder for them.

A naive licensing algorithm

The naive licensing algorithm is a very simple implementation of checking the validity of a license associated with the name of the user who has purchased the associated software. It is NOT an industrial strength algorithm, it has just demonstrative power, while trying to provide insight on the actual responsibilities of a real licensing algorithm.

Since the license checking code is usually shipped with the software product in compiled form, I'll put in here both the generated code (in Intel x86 assembly) since that is what the crackers will see after a successful disassembly of the executable but also the C++ code for the licensing algorithm. In order to not to pollute the precious paper space with unintelligible binary code I will restrain myself to include only the relevant bits of the code, with regard to the parts which naively determines whether a supplied license is valid or not, together with the C++ code, which was used to generate the binary code.

The following is the source code of the licensing algorithm:

cpp
static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
bool check_license(const char* user, const char* users_license)
{
    std::string license;
    size_t ll = strlen(users_license);
    size_t l = strlen(user), lic_ctr = 0;
    int add = 0;

    for (size_t i = 0; i ,std::allocator >::_Tidy (0FC1944h)  
    00FC161D  mov         al,bl  
}
    00FC161F  call        _EH_epilog3_GS (0FC2F7Ch)  
    00FC1624  ret  
    00FC1625  xor         bl,bl  
    00FC1627  jmp         check_license+0CAh (0FC1611h)

Let's analyze it for a short while. The essence of the validity checking happens at the address 00FC15F8 where the comparison cmp al, byte ptr [edx+0FC42A4h] takes place (for those wondering, edx gets its value as being the remainder of the division at 00FC15F0).

At this stage the value of the al register is already initialized with the value of license[lic_ctr] and that is the actual comparison to see that it matches the actually expected character. If it does not match, the code jumps to 0FC1625h where the bl register is zeroed out (xor bl, bl) and from there the jump goes backward to 0FC1611h to leave the method with the ret instruction found at 00FC1624. Otherwise the loop continues.

The most common way of returning a value from a method call is to place the value in the eax register and let the calling code handle it, so before returning from the method the value of al is populated with the value of the bl register (via mov al, bl found at 00FC161D).

Please remember, that if the check discussed before did not succeed, the value of the bl register was 0, but this bl was initialized to 1 (via mov bl,1 at 00FC160F) in case the entire loop was successfully completed.

If we think from the perspective of an attacker, the only thing that needs to be done is to replace in the executable the binary sequence of xor bl,bl with the binary code of mov bl,1. Since luckily these two have the same length (2 bytes) the crack is ready to be published within a few seconds.

Moreover, due to the simplicity of the implementation of the algorithm, a highly skilled cracker could easily create a key-generator for the application, which would be an even worse scenario, since the cracker didn't had to to modify the executable, thus further safety steps, such as integrity checks of the application would all be executed correctly, but there would be a publicly available key-generator which could be used by anyone to generate a license-key without ever paying for it, or malicious salesmen could generate counterfeit licenses which they could sell to unsuspecting customers.

Here comes in picture our C++ Obfuscating framework.

The C++ Obfuscating framework

The C++ obfuscating framework provides a simple macro based mechanism combined with advanced C++ template meta-programming techniques for relevant methods and control structures to replace the basic C++ control structures and statements with highly obfuscated code which makes the reverse engineering of the product a complex and complicated procedure.

By using the framework the reverse engineering of the license checking algorithm presented in the previous paragraph would prove to be a highly challenging task due to the sheer amount of extra code generated by the frameworks engine.

The framework has adopted a familiar, BASIC like syntax to make the switch from real C++ source code the the macro language of the framework as easy and painless as possible.

Functionality of the framework

The role of the obfuscating framework is to generate extra code, while providing functionality which is expected by the user, with as little as possible syntax changes to the language as could be achieved.

The following functionalities are provided by the framework:

  • wrap all values into a valueholder class thus hiding them from immediate access
  • providing a BASIC like syntax for the basic c++ control structures (if, for, while ...)
  • generating extra code to achieve complex code making it harder to understand
  • offering a randomization of constant values in order to hide the information

Debugging with the framework

Like every developer who has been there, we know that debugging complex and highly templated c++ code sometimes can be a nightmare. In order to avoid this nightmare while using the framework we decided to implement a debugging mode.

In order to activate the debugging mode of the framework define the OBF_DEBUG identifier before including the obfuscation header file. Please see at the specific control structures how the debugging mode alters the behaviour of the macro.

Using the framework

The basic usage of the framework boils down to including the header file providing the obfuscating functionality

cpp
#include "instr.h"`

then using the macro pair OBF_BEGIN and OBF_END as delimiters of the code sequences that will be using obfuscated expressions.

For a more under the hood view of the framework, the OBF_BEGIN and OBF_END macros declare a try-catch block, which has support for returning values from the obfuscated current code sequence, and also provides support for basic control flow modifications such as the usage of continue and break emulator macros CONTINUE and BREAK.

Behind the scenes: OBF_BEGIN and OBF_END

OBF_BEGIN expands to:

#define OBF_BEGIN try { obf::next_step __crv = obf::next_step::ns_done; std::shared_ptr __rvlocal;

and OBF_END becomes:

#define OBF_END } catch(std::shared_ptr& r) { return *r; } catch (...) {throw;}

In order to support for "return"-ing a value from the current obfuscated block we need a special variable __rvlocal. At later stages this value will be populated with meaningful values as a result of executing the code of the RETURN macro (which will "throw" a value with type of std::shared_ptr). The OBF_END will catch this specific value and handle it appropriately, while all other values thrown will be re-thrown in order to not to disturb the client code's exception handling.

Value and numerical wrappers

To achieve an extra layer of obfuscation, the integral numerical values can be wrapped in the macro N() and all integral numeric variables (int, long, ...) can be wrapped in the macro V() to provide an extra layer of obfuscation for doing the calculation operations. The V() value wrapper also can wrap individual array elements(x[2]), but not arrays (x) and also cannot wrap class instantiation values due to the fact that the macro expands to a reference holder object.

The implementation of the wrappers uses the link time random number generator provided by [Andrivet] and the values are obfuscated by performing various operations to hide the original value.

And here is an example for using the value and variable wrappers:

cpp
int a, b = N(6);
V(a) = N(1);

After executing the statement above, the value of a will be 1.

The value wrappers implement a limited set of operations which you can use to change the value of the wrapped variable. These are the compound assignment operators: +=, -=, *=, /=, %=, >=, &=, |=, ^= and the post/pre-increment operations -- and ++. All of the binary operators +, -, *, /, %, &, |, > are also implemented so you can write V(a) + N(1) or V(a) - V(b).

Also, the assignment operator to a specific type and from a different value wrapper is implemented, together with the comparison operators.

As the name implies, the value wrappers will wrap values by offering a behaviour similar to the usage of simple values, so be aware, that variables which are const values can be wrapped into the V() wrapper however as with real const variables, you cannot assign to them. So for example the following code will not compile:

cpp
    const char* t = "ABC";
    if( V(t[1]) == 'B')
    {
        V( t[1] ) = 'D';
    }

And the following

cpp
    char* t = "ABC";
    if( V(t[1]) == 'B')
    {
        V( t[1] ) = 'D';
    }

will be undefined behaviour because the compiler highly probably will allocate the string "ABC" in a constant memory area (although I would expect your compiler to choke heavily on this expression since it's not valid modern C++ anymore). To work with this kind of data always use char[] instead of char*.

Behind the scenes of the implementation of the numeric wrapping

The N macro is defined like the following:

cpp
#define N(a) (obf::Num::value ^ a>().get() ^ obf::MetaRandom::value)

As a first step let's consider that due to the implementation of [Andrivet] and the (more or less standard) __COUNTER__ macro the obf::MetaRandom::value and obf::MetaRandom::value) will have the same value.

Now, taking the obf::Num class in the visor:

cpp
template class Num final
{
public:
    enum { value = ( (n & 0x01)  | ( Num > 1)>::value ::value;
    }
    T get() const { volatile T x = v ^ MetaRandom::value; return x;}
private:
    volatil

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C++

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言