← [Chapter 2] [Chapter 4] →

3. Response Method Guidelines


3.1 Responses

With DS Protect, it is meaningless to only detect illegal usage and not respond to the results of that detection. The response itself should be something that makes it difficult for the program to continue, but using inappropriate response methods will drastically reduce the strength of DS Protect. Therefore, carefully consider response methods only after thoroughly studying this chapter.

These guidelines are established to reduce piracy problems. However, following these guidelines does not guarantee that all problems will be avoided.


3.2 Basics


3.2.1 Why Do I Need To Be Careful with Response Methods?

DS Protect provides a means for detecting piracy devices. However, the way you respond to those detection results must be suited to the program in question. You must therefore create your own response processing, and you must carefully consider how it will work. Focusing on the approach taken by attackers will make this easier to understand.

First, attackers observe the effects of your response that are visible on the LCD screens. Then they will search the ROM image binary for the processing that produces that response. After they have successfully determined the locations of the response processing, they can make a closer investigation of the surrounding program using tools such as disassembly lists. Then, they will attempt to create a patch (see Note) that will rewrite the program to bypass your response processing.

The key point here is even without knowing the fine details of your detection method, attackers can still successfully defeat your protection by focusing on your method of response. Because your response methods are the first things attackers target, the strength of DS Protect will be drastically compromised if your response is improperly applied.

Note: A patch is a program that revises a part of another program.


3.2.2 Time Until Response

We recommend designing a response that is possible to confirm within 10 to 20 minutes after starting up the game. This makes formal testing of the operation possible.

A response that occurs midway through the game would probably make for effective protection, but testing it would be difficult. Consider that type of response only if you understand the risks.


3.2.3 Probability That Response Will Occur

You must respond every time illegal use is detected. A design using random numbers, where a response may not occur every time, is good from the standpoint of making it more difficult to circumvent protection. However, we do not recommend such designs because formal testing may not be possible.


3.2.4 Record of Response Processing

Always keep a record of (1) when detection occurs, (2) when the response occurs, and (3) how the response can be checked.


3.2.5 Simple Response Methods

The probability that DS Protect will be compromised in a short amount of time is dramatically higher if you implement a simple response method, so a simple method is not recommended at all. This is described in more detail later.


3.2.6 Recommendation for Compression of ARM9 Module

To protect against cracking the code, we recommend that you compress the ARM9 static or overlay module into which the piracy device detection code is expanded.

See compstatic(.TWL) in the Function Reference and Build Switches in NITRO-SDK or TWL-SDK for details on module compression.
・NITRO-SDK or TWL-SDK Function Reference Manual → Tools and Commands → Tools Related to ROM Images → compstatic(.TWL)
Nitro(TWL)SDK/docs/SDKRules/Rule-Defines.html, Keyword "NITRO(TWL)_COMPRESS"

If you are using CodeWarrior for Nintendo DS, go to Nitro TS ROM Settings. Under Nitro Makerom Postlinker in the linker tree, select the call compstatic tool option, and then set options: to -c -9 -F main.sbin main_defs.sbin main_table.sbin. This enables compression of both modules.

If you are using CodeWarrior for Nintendo DSi, go to TWL TS ROM Settings. Under TWL Makerom Postlinker in the linker tree, select the call compstatic tool option, and then set options: to -9 -c -a -e_LZ -f "%OUTPUT_FOLDER%\component.files". This enables compression of both modules.

After building, you can check whether the modules are compressed correctly in the ROM image files using the DS-CCC tool included with this SDK. For more information, see section 5.4.1.


3.2.7 Recommendation for Placement in Overlay Module

To protect against cracking the code, we strongly recommend that you place functions that call piracy device detection code and other detection code in an overlay module that does not reside in memory very long, not on the ARM9 static module that always exists in main memory.

We have confirmed cases of some piracy devices circumventing detection when these functions are located in the ARM9 static module. For more information, see section 1.2.1.


3.3 Formulating Response Methods

Understanding inappropriate response methods is essential in formulating your own response methods. This section explains both inappropriate response methods and implementable methods.


3.3.1 Examples of Inappropriate Response

In this section, an inappropriate response method is behavior that makes it easy to observe that piracy device detection processing exists. Examples are shown below.

1) Displaying messages such as "Piracy device detected!"
You must never use your own specialized error messages. Display data such as error messages exists in the ROM image as plaintext character data, so searching for such text is easy. This makes it easy to determine detection and response timing, and that can be used as a foothold for an attack. Searching becomes slightly more difficult if the error messages are prepared as images, but this is still not a good method.

2) Response capable of displaying messages
For the same reasons given above, we do not recommend that you display any sort of messages scolding players for copying software. Not only does this advertise the existence of your piracy device detection, but that unique message data gives hints to the attacker on ways to circumvent protection. Depending on the content of your message, it might go so far as to invite user hostility.

3) Stopping the game at a predictable point
For example: the game starts up, lets the user play for exactly 20 minutes, then displays a message and exits, or; the game lets the user clear one stage, then displays a message and exits. This behavior is so predictable it is difficult to believe it is a normal bug. This behavior by itself is sufficient to make the user suspect that the game incorporates piracy device detection. Examples of possible response timing are given later.

4) Requiring a long time before response
DS Protect is meant to keep people from playing games for free. This includes all illegal users, from light users that make so-called casual copies (see definition below) to hardcore gamers. Therefore, timing your response to occur late, such as just before the last level or during the second run-through, allows users to play a majority of the game. Such protection is meaningless. It is also not recommended from a testing perspective. However, implementing multiple detection checks, such as one at the beginning and one halfway through, has been determined to be a successful method.

Casual copies: Copies made by a user who is unaware that copying is illegal.

5) Taking a long time between detection and response
This was explained in section 3.2.2. We do not recommend that you implement designs where confirming your response takes a long time, because these are harder to test properly.

6) Response affecting game saves, in games in which saving is not important
The level of protection is low if your response occurs at save time in games in which saving is not important or in games that the user can easily clear without having to save. We do not recommend designs that allow users to bypass protection by performing or not performing user operations.

7) Sending username or birthdate information covertly using Wi-Fi Connection or some other connection
Never use a response that could be considered excessive or that resembles malware behavior (see definition below). This includes but is not limited to illegal transmission of personal information. If implementation of such features were discovered, it would certainly seriously degrade your corporate image.

Malware: An all-inclusive term describing software that is malicious, such as computer viruses, worms, and spyware applications.


3.3.2 Examples of Inappropriate Implementation

In this section, an inappropriate implementation is one that allows the program to be easily reverse-engineered. Examples are shown below.

1) Maintaining the detection result in a global variable defined to store detection results
To run response processing, it is necessary to maintain the detection result somewhere until the response is executed. If you use a global variable, the memory region is statically allocated and it becomes extremely easy to use a disassembler to identify the locations being referenced. This decreases the strength of your protection. Refer to the samples described later in sections 3.4.4 and 3.4.5.

2) Using or referencing strings within your response processing that could provide hints
ASCII strings are the information most easily obtained from binary data. This holds true for raw binary images as well as ROM images. For that reason, almost all attackers will try to infer functionality using utilities such as the UNIX strings command or the string reference features available in advanced disassemblers. This is the reason why we have repeatedly stated that you must not use messages such as Piracy device detected! As referred to here, strings include error messages and dialogue spoken by characters, but also include non-user-facing text such as ERROR:%08d and other output format specifiers used by the sprintf function.

3) Coding response processing such that the result of the response can be known immediately after detection
The program structure from detection to response is necessarily simple if you use processing that makes the result of the response known immediately after detection, so this is not an appropriate method. Whenever possible, locate your response processing among unrelated processing.


3.3.3 Examples of Appropriate Implementation

It is difficult to present specific samples here. However, we can say the following with certainty.

A response that reproduces a bug when illegal usage is detected is most effective when it is difficult to debug at the binary level.

Consider this fact when you formulate your response methods.


3.4 Basic Response Examples

Examples of basic detection processing using DS Protect follow.

Although the information disclosed in this manual is confidential and private, you should expect that attackers have also read this manual. If you take protection seriously, you must move to a higher level of protection than the examples implemented here. Use the methods introduced here for reference, but it is best to use them as hints that allow you to develop more advanced mechanisms of your own.

If you use your detection code repeatedly, insert it in locations as widely separated as possible. It is valid and effective to use the same detection functions in multiple separate locations. However, none of the detection functions are thread-safe.


3.4.1 Immediate Execution (Not Recommended)

This is the simplest method and therefore not recommended. However, we use it as an example to help explain how the functions operate.

In this example, the ProtectFunc function is called immediately upon detection of a piracy device. Put the response processing in the ProtectFunc function.

// Function to run when a piracy device is detected.
void ProtectFunc()
{
// Response processing goes here
}

// ...

void Func(void)
{
// Separate the various detection processes from each other, and separate detection processing from everything else

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ProtectFunc() is called when piracy device is detected
AM_IsMagiconA2(ProtectFunc);

// ...

// ProtectFunc() is called when a piracy device is detected
// However, the A3 version is a dummy function, so it never detects anything
AM_IsMagiconA3(ProtectFunc);

// ...

// When a piracy device is detected, the instructions inside the if statement are run
if (!AM_IsNotMagiconA1(NULL))
{
ProtectFunc();
}

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...

}

3.4.2 Delayed Execution

This example sets the product of two prime numbers (139 * 41) as the initial value and rewrites this value only if a piracy device is detected. When detection of a piracy device is successful, the Magicon value is changed. Only then does response processing run. The idea is to express something that ought to be a Boolean value (either 0 or 1) as something other than a Boolean value.

void Func()
{
// Set the product of two prime numbers as the initial value
u32 Magicon = 139 * 41;    // You must ensure that this value gets changed

// ..

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// Only if the program is running on a piracy device will the u32-type return value be nonzero and the initial value be changed.
// 71 is also a prime number.
    Magicon += 71 * AM_IsMagiconA1(NULL);

    // Unload the library that was read as an overlay
    FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

    // ...

    // Only if the program is running on a piracy device will the initial value change, so response processing is run.
    if (Magicon % 139)
    {
        // Response processing goes here
    }

    // ...

    if (Magicon % 41)
    {
        // Response processing goes here
    }

    // ...

}

3.4.3 Process Branching

Here, the basic flow is branched by fragmenting the main loop process.

// Main loop when the software is being used illegally
void ProtectMainLoop()
{
// Main loop processing when a piracy device is detected goes here
}

// Main loop processing when the software is being used legally
void MainLoop()
{
// Put normal main loop processing here
}

void Func()
{

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// When the software is being used legally, the normal main loop is called
AM_IsNotMagiconA1(MainLoop);

// ...

// When the software is being used illegally, the main loop for piracy devices is called
AM_IsMagiconA1(ProtectMainLoop);

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...

}

3.4.4 Initializing Only When Software Is Being Used Legally

With this method, the program is properly initialized only when it is being used legally. When it is being used illegally, it is not properly initialized and is thus prevented from continuing. Here, initialization includes both the execution of an initialization function and the initialization of variables.

Not only does it become unnecessary to write code for response processing, but creativity allows you to increase the in-program separation between detection and response, so the protection this provides will probably be more difficult to defeat. However, you must know how the program behavior will change when initialization does not occur.

// Initialization function
// It is assumed that if this function is not run, the program won't operate correctly
void FuncInit()
{

// Initialization processing goes here

// ・Initialize screen display
// ・Initialize character parameters
// ・Initialize event flags
// etc...

}

void Func()
{

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// Put initialization processing inside the FuncInit function
// When the software is being used legally, initialization processing is called
AM_IsNotMagiconA1(FuncInit);

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// You must ensure that the program will malfunction if it is not initialized
// ・Cause screen display problems
// ・Characters no longer align
// ・Player can't obtain event items
// ・Events don't progress
// etc ...

// ...
}

3.4.5 Initializing Incorrectly When Software is Being Used Illegally

This method is the inverse of the previous method. Here the program is first initialized normally and then initialized again with incorrect values if it is being used illegally.

// Initialization function
void FuncInit()
{

// Initialization processing goes here

}

void FuncProtectInit()
{

// Initialization processing with incorrect values goes here
// ・Modify the data so nothing makes sense
// ・Modify the data so the player can't obtain event items
// ・Modify the data so events don't progress
// etc...

}

void Func()
{

// ...

// Initialize
FuncInit();

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// Put incorrect initialization processing inside the FuncProtectInit function
// When the software is being used illegally, the incorrect initialization processing is called
AM_IsMagiconA1(FuncProtectInit);

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...
}

3.4.6 Combinations of Response Patterns

You can expect the efficacy of your protection to improve if you use combinations of response patterns.

// Initialization function
// It is assumed that if this function is not run, the program won't operate correctly
void FuncInit()
{

// Initialization processing goes here

// ・Initialize screen display
// ・Initialize character parameters
// ・Initialize event flags
// etc...

}

// Main loop when the software is being used illegally
void ProtectMainLoop()
{
// Main loop processing when a piracy device is detected goes here
}

// Main loop processing when the software is being used legally
void MainLoop()
{
// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

AM_IsNotMagiconA1(FuncInit());

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// Put processing here that won't operate correctly if the program is not initialized

}

void Func()
{

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// When the software is being used illegally, the main loop for piracy devices is called
AM_IsMagiconA1(ProtectMainLoop);

// ...

// When the software is being used legally, the normal main loop is called
AM_IsNotMagiconA2(MainLoop);

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...

}

3.5 Examples of Response Timing

This section provides sample responses. Also, see section 3.4 for information on implementation.

Let us repeat this point:

A response that reproduces a bug when illegal usage is detected is most effective when it is extremely difficult to debug.

Formulate the timing of your response based on your own debugging experiences.


3.5.1 Save-Type Protection

Historically, piracy devices frequently had bugs that prevented them from handling save data correctly. Currently, there is a trend for fewer such bugs, but methods that induce save-data-related errors are thought to provide significant protection against piracy.

1) Make the Continue item unavailable
2) Cause loading to fail
3) Cause saving to fail
4) Write illegal use flags to the save data

These are just a few conceivable response methods.


3.5.1.1 Example of Disabling the Backup Device [BD-01]

[BD-01] Example Implementation to Disable Backup Device Functionality When Software is Being Used Illegally

This is an example of restricting access to the backup device and preventing the screen transition to continue from the load screen only when the software is being used illegally.

Here, detection and response occur during the period from game startup to the title screen and initial menu screen. The Locations Requiring Implementation Check in the flowchart are places where your new implementation should go.

3.5.1.1.1 Hints for Implementation at Location Requiring Implementation Check (1) in the Flowchart

Here, normal access to the backup device is locked when illegal use is detected by replacing the backup device feature during initialization.

The following example uses overrides as the method for replacing the backup device features. However, if this method's implementation branches based on the detection results just prior to loading and saving, it is easy to modify the program to bypass protection. Therefore this is not a preferred method.

// Backup device management class
class BackupDeviceManager {
public:
void Read() {
// Function that reads data from backup device

// Processing goes here

}

void Write() {
// Function that writes data to backup device

// Processing goes here

}
};

// Backup device management class that is inherited when a piracy device is being used
class BackupDeviceManager_Dummy :public BackupDeviceManager {
public:
void Read() {

// Override this with processing that performs incorrect operations

}

void Write() {

// Override this with processing that performs incorrect operations

}
};
// Backup device management class.
BackupDeviceManager *gpBackupManager;

// ...

// Initialization processing for when software is being used legally
void InitObject() {

// Initialize with normal, correct class
*gpBackupManager = new BackupDeviceManager();

}

// Initialization processing for when software is being used illegally
void InitObject_Dummy() {

// Initialize with class inherited when piracy device is being used
*gpBackupManager = new BackupDeviceManager_Dummy();

}

// Object initialization
void Init() {

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

if (!AM_IsMagiconA1(InitObject_Dummy)) {
InitObject();
}

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...

}
3.5.1.1.2 Hints for Implementation at Locations Requiring Implementation Check (2) in the Flowchart

See section 3.5.2.1 Example Implementation That Modifies Screen Transition When Software Is Being Used Illegally.





3.5.2 Menu-Type Protection

This method changes the menu structure and operations depending on whether the software is being used legally or illegally.

1) Make the Start Game item unavailable
2) Remove the Continue item (don't just cause an error when it is selected; make it not exist at all)
3) Make it impossible to transition out of the menu screen

These are just a few conceivable response methods. If these methods perform conditional branching when a menu item is selected, they are vulnerable to instruction rewriting. Instead of conditional branching it is better to rewrite the screen transition information when the menu is initialized. One example is provided.


3.5.2.1 Example Implementation That Modifies Screen Transition When Software Is Being Used Illegally [SC-01]

[SC-01] Example Implementation That Modifies Screen Transition When Software Is Being Used Illegally

When the software is being used illegally, this method includes processing that rewrites the screen transition information so that the screen will not transition normally. (The player cannot leave the menu and go to the actual game).

The method shown below performs detection of illegal use when a menu item is selected. This detection timing is not recommended. Attackers can focus on the branch processing of isMagicon and counter your response simply by inverting the conditions.

// When the player selects an item on the initial menu screen,
// this processing sets the next screen based on the cursor position
switch (cursor) {
case 0:
    //"Start Game" was selected
nextState = stateGameStart;
break;
case 1:
    //"Continue" was selected
    // With this implementation, just flipping the determination of isMagicon is enough to
    // counter your response, so this is not the best method. (Alternatives are given later.)
if (isMagicon) {
// When illegal use is detected, make it so that "Start Game" was selected instead
nextState = stateGameStart;
} else {
nextState = stateContinue;
}
break;
case 2:
    //"Configuration" was selected
nextState = stateConfigration;
break;
case 3:
    //"Wi-Fi Connection" was selected
nextState = stateWifiConnection;
break;
}

With this method, it is better to integrate your response into the initialization of screen transition information.

// 'State*' inherits the 'State' class, which defines the screen

// Initialization for when software is being used legally
void InitState(void) {

// ...

stateGameStart = new StateGameStart();
// When the player selects the "Continue" menu item,
// transition correctly to the "Continue" screen
stateContinue = new StateContinue();
stateConfigration = new StateConfigration();
stateWifiConnection = new StateWifiConnection();

// ...

}

// Initialization for when software is being used illegally
void InitState_Magicon(void) {

// ...

stateGameStart = new StateGameStart();
// Even when the player selects the "Continue" menu item,
// transition to the "Start Game" screen instead
stateContinue = new StateGameStart();
stateConfigration = new StateConfigration();
stateWifiConnection = new StateWifiConnection();

// ...

}

// ...

void func(void) {

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// Initialize screen transitions
// When the software is being used illegally, the InitState_Magicon function is called
// When the software is being used legally, the InitState function is called
if (!AM_IsMagiconA1(InitState_Magicon)) {
InitState();
}

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...

}



3.5.3 Online-Match-Type Protection

[NW-01] Example Implementation to Reject Online Matches When Software is Being Used Illegally

Since piracy devices can also be used to cheat, it may be good to consider restricting online matches.

1) Display the Online matches are not possible message normally displayed when the user tries to connect without configuring network settings
2) Prohibit wireless and Wi-Fi connections, or shut down the system

These are just a few conceivable response methods. These responses occur immediately after detection, but the effects of the response cannot be visually observed until connection processing has run.

As an example, see the Example Implementation to Reject Online Matches When Software is Being Used Illegally flowchart. It is preferable for the system to behave as though there has been a connection error.




3.5.4 Event-Type Protection

These response methods affect how and whether events progress. Although your response must be very well-defined so that operation can be properly tested, depending on the mechanisms you use, you can prevent attackers from realizing what is happening for a relatively long time.

1) Make the initial event item impossible to find
2) Make characters no longer align
3) Make it so the player can never play past the first stage
4) Make it so the available menu items do not increase even when the player accomplishes the required tasks, and always return the game to the title screen.

These are just a few conceivable response methods. With respect to suggestion number 4, where the game always returns to the title screen, you can make the response easier to test by including a slight variation from normal behavior which does not, in itself, tip off attackers to the existence of protection.

void ResetParameter()
{

// Reset or modify parameters and flags
// ・Enemies no longer drop event items
// ・Disable effects of event items
// ・etc...

}

void Func()
{
// ...

// Normal initialization processing
InitParameter();

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

AM_IsMagiconA1(ResetParameter);

// ...

AM_IsMagiconA2(ResetParameter);

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...
}

3.5.5 Bug-Inducing Protection

This type of protection intentionally causes a bug to occur when the software is being used illegally. Several methods are possible, such as causing infinite loops or infinite recursion, or preventing screens from being displayed.

・ Cause memory leaks
・ Intentionally shrink the heap size obtained at initialization
・ Cause an infinite loop to prevent game startup
・ Cause the program to hang
・ Cause a scrambled screen display

These are just a few conceivable response methods. The following code is an example that induces a memory leak when the software is being used illegally.

To repeat once more: Reproduced bugs are most effective when they are extremely difficult to debug at the binary level.

Formulate your response methods based on your own debugging experiences.

void Func()
{

u8 *buffer;

// ...

// Use an overlay to invoke a module that includes the library
FS_LoadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

if (AM_IsMagiconA1(NULL)) {
// Intentionally induce a memory leak
// Don't forget you must also confirm that this will stop the program
buffer = new u8[0x10000];
}

// Unload the library that was read as an overlay
FS_UnloadOverlay(MI_PROCESSOR_ARM9, FS_OVERLAY_ID(OVERLAY_PROTECT));

// ...

}

 

← [Chapter 2] [Chapter 4] →


CONFIDENTIAL