> For the complete documentation index, see [llms.txt](https://documentation.virbox.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://documentation.virbox.com/protect-static-library-and-object-file/solution-overview-protection-scheme-for-arm-thumb-object-files-and-static-libraries.md).

# Solution Overview-Protection Scheme for ARM Thumb Object Files and Static Libraries

**Summary**

This document presents a customized protection scheme of **Virbox Protector** designed for ARM‑based embedded environments, specifically targeting the [**Thumb instruction set**](https://developer.arm.com/documentation/ddi0210/c/CACBCAAE). and fully supporting static libraries and object files generated by the [**IAR compiler**](https://en.wikipedia.org/wiki/IAR_Systems).

Since many embedded vendors rely on IAR IDE for commercial ARM embedding device development, this compatibility ensures that `.o` and `.a` files produced by IAR can be protected without modifying the customer’s toolchain or build workflow.

[Virbox Protector](https://appsec.virbox.com/) provides a comprehensive workflow for securing [**object files**](https://developer.arm.com/documentation/101655/0961/Cx51-User-s-Guide/Compiling-Programs/Command-Prompt/Output-Files/Object--OBJ--File) (`.o`) and [**static libraries**](https://en.wikipedia.org/wiki/Static_library) (`.a`) commonly used in customer delivery scenarios.

The solution integrates multiple protection techniques—including code virtualization, obfuscation, watermark embedding, function‑symbol optimization, and object‑file merging—to enhance resistance against reverse engineering while maintaining compatibility with ARM toolchains and constrained flash environments.

To address customer feedback regarding file size expansion, especially in systems with strict flash limits (e.g., 128 KB), this document also details several optimization strategies. These include reducing redundant virtual machines, trimming high‑impact obfuscation features, and improving function parsing to minimize unnecessary branches.

All test data, comparisons, and risk assessments included in this document serve as reference points. Actual results may vary depending on the customer’s real application, build environment, and selected protection configuration.

### Thumb Virtual Machine Function Verification Workflow

#### Product Background

`Virbox Protector` supports function‑level protection for object (`.obj`) files, including both code obfuscation and code virtualization:

* **Code obfuscation** transforms original instructions into randomized and unreadable instruction fragments through techniques such as equivalent substitution, immediate value encryption, indirect jumps, fake branches, junk instruction insertion, and instruction slicing. Obfuscation does **not** change the fundamental characteristics of the instruction set: ARM instructions still execute as ARM, and Thumb instructions still execute as Thumb on the system. Since the original execution flow is preserved, obfuscation is **not affected by special hardware environments**.
* **Code virtualization** converts the original assembly instructions within a function into custom virtual instructions. These instructions are executed inside a custom virtual machine at runtime, simulating memory access, conditional branching, register states, and more. This requires precise instruction parsing and handling of different instruction formats. `Virbox Protector` uses the **A32 instruction set** to implement a virtual machine interpreter capable of handling **A32, T16 (Thumb), and T32 (Thumb‑2)** instructions. However, some embedded processors (e.g., Cortex‑M4 based on the ARMv7E‑M architecture) **cannot execute A32 instructions**, which prevents the protected program from being properly virtualized on such devices.

To expand the protection coverage and enhance security on specific processors, `Virbox Protector` introduces a customized virtual machine implementation that supports virtualization of Thumb instructions on these platforms.

#### Mechanism

For object files containing Thumb instructions, the [Virbox Protector](https://appsec.virbox.com/) uses **T16 and T32 instructions** to implement a virtual machine interpreter for the **T16 (Thumb) and T32 (Thumb‑2)** instruction sets. During protection, the **Thumb virtual machine is enabled by default**, allowing protected Thumb‑based object files to run correctly on specific processors (such as Cortex‑M4 processors based on the ARMv7E‑M architecture).

#### Feature Testing

**Test Environment**

**Android NDK Compilation**

When compiling ARM32 programs with the Android NDK, adding the `-mthumb` parameter instructs the compiler to generate Thumb‑instruction binaries. This allows the resulting program to run on Android devices while still producing Thumb‑based object files, enabling us to perform testing even without embedded processor hardware.

Test Procedure:

1. Compile the Thumb‑instruction `.obj` file using the Android NDK.
2. Use the [Virbox Protector](https://appsec.virbox.com/) to protect the `.obj` file, selecting the target functions for code virtualization.
3. Inspect the protection results of the Thumb instruction set in the protected `.obj` file.
4. Verify whether the protected program can be successfully linked and compiled, and confirm that the resulting executable runs correctly.

***

[**IAR Embedded Workbench**](https://www.iar.com/embedded-development-tools/iar-embedded-workbench) **Compilation**

Test Procedure:

1. Compile the Thumb‑instruction `.obj` file using the [**IAR Embedded Workbench**](https://www.iar.com/embedded-development-tools/iar-embedded-workbench) **Compilation**
2. Use the [Virbox Protector](https://appsec.virbox.com/) to protect the `.obj` file, selecting the target functions for code virtualization.
3. Inspect the linking behavior of the protected `.obj` file when generating the final executable.
4. Inspect the protection results of the Thumb instruction set in the protected `.obj` file.

**Project Decompiled Comparison (Before & After Protection)**

**Decompiled View Before Protection**

Locate the function intended for virtualization and directly decompile it to observe the original assembly instructions.

<figure><img src="/files/gzx7VKgJV2B7TbsuEIyy" alt=""><figcaption></figcaption></figure>

View the C–style pseudocode generated after decompiling the function.

<figure><img src="/files/Y8qk3Pl5RfravhcVHe9s" alt=""><figcaption></figcaption></figure>

**Decompiled project After Protection**

1. Invocation Process

* Locate the virtualized function. When the program executes the instruction `BL sub_D8E4`, double‑clicking `BL sub_D8E4` saves the return address and jumps to `B.W loc_13626`.
* As shown in the figure:

<figure><img src="/files/2o9VHOTUKka7oRvmqNTy" alt=""><figcaption></figcaption></figure>

2. Intermediate Jump

* Double‑click `B.W loc_13626` to continue the jump (an unconditional branch that does not affect the LR register) to the new location. The `B.W` instruction is used in Thumb‑2 mode.
* As shown in the figure:

<figure><img src="/files/Abs6hsp2KlMqkEmFsiZ3" alt=""><figcaption></figcaption></figure>

3. Final Execution

* After reaching `loc_13626`, the program begins executing the actual instructions at this location. The resulting instruction sequence (the final target code) consists entirely of virtualized Thumb instructions.
* As shown in the figure:

<figure><img src="/files/mU6nFPBAqn0kC834jypX" alt=""><figcaption></figcaption></figure>

View the C‑style pseudocode generated after decompiling the function.

<figure><img src="/files/skgvsi93gKpyCO9VyQIh" alt=""><figcaption></figcaption></figure>

#### Performance Testing

**File Size Increment Test**

This test evaluates the file size increase before and after protection. Using a sample file (`system_machine.o`), code virtualization is applied to observe the file size changes when virtualizing: All functions **versus** virtualizing: Only selected functions.

See the table below:

| Number of Virtualized Functions | Original File Size | File Size After Protection | Increase |
| ------------------------------- | ------------------ | -------------------------- | -------- |
| Any 10 selected functions       | 101,161            | 157,828                    | 56,667   |
| Any 30 selected functions       | 101,161            | 213,516                    | 112,355  |
| All functions (134 total)       | 101,161            | 443,532                    | 342,371  |

> **Notes:**
>
> 1\) Compare the file size before and after protection, and choose an appropriate protection strategy based on the actual device constraints. 2) The more functions selected for virtualization, the larger the resulting file size and the greater the performance impact. Adjust dynamically according to the program’s actual requirements.

**Startup Time**

**1. Description**

Using the test demo as an example, compare the performance of the original program and the program after virtualizing 11 functions.

**2. Results**

Note: Performance overhead varies depending on the specific code logic.

**Startup time of the original program:**

<figure><img src="/files/8MhSWjhgWL3QAVjmQZSV" alt=""><figcaption></figcaption></figure>

> Note:
>
> 1. The titles of the four columns in the table above are:
>
> Process ID (PID) , Turnaround Time, Waiting Time, Response Time;
>
> 2. For total 5 Process (PID) above:
>
> Average Turnaround Time: 4.80s;
>
> Average Waiting Time: -1.80s;
>
> Average Response Time: 3.80s;
>
> program execution time:0.004531s

**Startup time of the fully virtualized program:**

<figure><img src="/files/FQVODWrWWNmmsnmg4Nc7" alt=""><figcaption></figcaption></figure>

> Note:
>
> For total 5 Process (PID) with all of function virtualization above:
>
> Average Turnaround Time: 4.60s;
>
> Average Waiting Time: -4.40s;
>
> Average Response Time: 3.60s;
>
> program execution time:0.007879s

**Compatibility Testing**

**Runtime Environment:** Android 7 – Android 16

**Test Program:** Android ARM32 (Thumb instructions)

**Description:** The software is tested across Android devices running different system versions to ensure stable operation.

| Device Model      | System     | Result |
| ----------------- | ---------- | ------ |
| Xiaomi MIX        | Android 7  | Pass   |
| Huawei nova youth | Android 8  | Pass   |
| Xiaomi MIX 2      | Android 9  | Pass   |
| Xiaomi 10         | Android 12 | Pass   |
| Google Pixel 6    | Android 16 | Pass   |
| Oppo Find X8      | Android 16 | Pass   |
| OnePlus 13        | Android 16 | Pass   |

### Multi‑VM Virtualization Function Verification

#### Mechanism

During protection, the `Virbox Protector` tool converts each basic block of a function into virtual machine pseudocode, which is then interpreted and executed by the VM interpreter at runtime.

In this enhanced mechanism, multiple virtual machine instances can be created during protection. Each function is randomly assigned to a different VM instance, and each VM uses a distinct pseudocode encoding scheme. This significantly increases the overall security level.

#### Feature Testing

Test Steps:

1. Compile Thumb‑instruction `.obj` files using the IAR and Green Hills toolchains.
2. Use the Virbox Protector (debug version with symbols) to protect the `.obj` file. Select the functions to be virtualized and configure the number of VM instances (e.g., 4 VMs) in the advanced options.
3. Examine the behavior of the linked executable generated from the protected `.obj` files.
4. Inspect the protection effect on the Thumb instruction set within the protected `.obj` files.

#### Verification of Protection

> Note: The debug version of the Virbox Protector (with symbols) is intended **only** for testing the multi‑VM functionality. The protected `.obj` files generated by this version **must not** be distributed externally.

Use the Virbox protector (debug version with symbols) to protect the `.obj` file. After protection, load the resulting `.obj` file into IDA and search for the `vm_init` function. The number of occurrences indicates how many VM instances are being used. For example, the following case shows that 8 VM instances were generated:

<figure><img src="/files/cZTmHbxBbzTJaKvhNSF2" alt=""><figcaption></figcaption></figure>

**Decompiled View After Protection**

1. Locate the protected function. The program execution reaches the instruction

<figure><img src="/files/PI4FrY0gMv1wrthOaL2O" alt=""><figcaption></figcaption></figure>

2. Double‑click to jump to `BL vm_init_0`.

<figure><img src="/files/hFYvckUdrYpcfDk9K3OJ" alt=""><figcaption></figcaption></figure>

3. Click **X** to enter the `vm_init_0` function and observe the differences in its instructions.
4. Following the same steps, you can compare the behavior of the other VM instances.

<figure><img src="/files/eysAEcLetD6vF9c7Zldo" alt=""><figcaption></figcaption></figure>

#### File Size Increment Test

The file `LP_D13_PTG_app_test.a` is protected to evaluate the file size increase before and after applying protection.

| Number of VM Instances | Number of Functions | Merge Option | File Size | Size Increase |
| ---------------------- | ------------------- | ------------ | --------- | ------------- |
| Original Program       | /                   | /            | 366k      | /             |
| 1 VM                   | 37                  | Enabled      | 509k      | 143k          |
| 1 VM                   | 37                  | Disabled     | 554k      | 188k          |
| 4 VMs                  | 37                  | Enabled      | 662k      | 296k          |
| 4 VMs                  | 37                  | Disabled     | 706k      | 340k          |
| 8 VMs                  | 37                  | Enabled      | 866k      | 500k          |
| 8 VMs                  | 37                  | Disabled     | 911k      | 545k          |

### Watermark Function Verification

#### Description

The `Virbox Protector` implements this feature by embedding custom data into the `.obj` file (object file or static library) during the protection process. These data fields can be configured in the tool interface. After the protected `.obj` file is compiled and linked, the embedded data will remain in the final binary as a watermark.

By detecting whether the binary contains this watermark, it is possible to determine whether the static library has been linked, thereby identifying the user of the static library and preventing unauthorized third‑party usage.

#### Waremark Setting

**Virbox Protector Version:** 3.5.1.21416

**Steps to set Watermark:**

1. Drag the `.obj` file into the `Virbox Protector` GUI tool.
2. In the Protection options of the Virbox Protector GUI tool, the watermark configuration panel is displayed. The watermark value can be set using hexadecimal characters.

> **Note:** The watermark value must contain an **even number of hexadecimal characters**. Odd‑length values are not allowed; otherwise, the protection process will fail.

<figure><img src="/files/XAAKCYNgkNJFd04BMMle" alt=""><figcaption></figcaption></figure>

#### Verification of Watermark setting

1. Use the `010 Editor` tool to inspect the watermark configured during protection. After parsing the static library or binary file, directly search for the corresponding Hex Bytes.
2. Enter the configured watermark value in the search field. If the watermark is stored in the `.text` code section after protection, the configured watermark bytes can be located within the code.

<figure><img src="/files/hlb1abYgHY8ZGFCiuVkP" alt=""><figcaption></figcaption></figure>

### Function Symbol Optimization

**Issue Description:**

When an `.obj` file is dragged into the `Virbox Protector` GUI tool, some function names displayed in the function list contain `??`, as shown in the example below:

<figure><img src="/files/gjxWtFKCnrE8NKc4mPbI" alt=""><figcaption></figcaption></figure>

**Optimization Approach:**

The `?? ... [number]` symbols generated by the IAR compiler are no longer treated as individual functions. In practice, this merges all branch functions into a single function, eliminating the appearance of branch‑level entries and preventing function names from being displayed as `??`. As a result, the total number of functions is reduced, and each optimized function contains more instructions compared to the pre‑optimization branch functions.

| Protector Version | Total Functions |
| ----------------- | --------------- |
| 3.5.1.21385       | 506             |
| 3.5.1.21416       | 115             |

**Virbox Protector Version fixed issue:**

In version **3.5.1.21416**, the function parsing result appears as shown below:

<figure><img src="/files/OimqN3TbbTwTgx2ILjfv" alt=""><figcaption></figcaption></figure>

### Excessive File Size Increase After Protection

For some of embedding device flash, the maximum available flash space is 128 KB. When more than 40 functions are selected for code obfuscation, the compiler reports insufficient flash space. Therefore, optimizations have been applied to the standard obfuscation feature and the object‑file merging feature to reduce the final protected program size.

#### Code Obfuscation Optimization Plan

Since the maximum flash capacity is confirmed to be 128 KB, the core objective is to **maximize security** (including future virtualization support) **while ensuring the protected binary does not exceed the 128 KB limit**. The current version adopts the following adjustment strategy:

**Obfuscation Adjustment Strategy**

*Note: The scoring scale is 1–10, where a higher score indicates a greater impact.*

| Obfuscation Method         | Description                                                                                                                | Impact Analysis                                                                                                      | Impact Score                             | Trimmed                |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ---------------------- |
| Equivalent Substitution    | Replaces original instructions with semantically equivalent but structurally different instructions.                       | Increases code complexity and typically increases code size.                                                         | No change                                | No                     |
| Immediate Value Encryption | Encrypts constants (immediate values) and decrypts them at runtime.                                                        | Prevents direct inspection of constants in the binary, increases static analysis difficulty, may increase code size. | No change                                | No                     |
| Indirect Jumps             | Replaces direct control‑flow jumps with indirect jumps (e.g., function pointers or jump tables).                           | Makes disassembly targets inaccurate, increases analysis difficulty, may increase code size.                         | No change                                | No                     |
| Fake Branches              | Inserts non‑executed branches (dead code) to confuse control flow.                                                         | Requires analysts to distinguish real vs. fake branches; significantly increases code size.                          | No change                                | No                     |
| Dummy Instructions         | Inserts meaningless (garbage) instructions to disrupt logical flow.                                                        | Increases code complexity and significantly increases code size.                                                     | Security Impact: 2/10 Space Impact: 8/10 | Yes (reduced quantity) |
| Instruction Slicing        | Splits a single instruction into multiple fragments, possibly placed in different locations.                               | Makes logic more scattered and harder to understand; may increase code size.                                         | No change                                | No                     |
| Anti‑Run‑Trace             | Inserts hidden probes to detect single‑step debugging; triggers incorrect instructions when illegal debugging is detected. | Adds probes that increase code size; mainly counters IDA Pro trace features; unnecessary for bare‑metal systems.     | Security Impact: 0/10 Space Impact: 6/10 | Yes (removed)          |

**Comparison Before and After Optimization**

**Test Procedure:**

Using the pre‑optimization version (3.5.0.21352) and the post‑optimization version (3.5.1.21361), code obfuscation was applied to a sample program (`system_machine.o`). The file size changes were measured when applying obfuscation to all functions and to selected functions.

**Reference Data:**

| Number of Obfuscated Functions | Program Size (3.5.0.21352) | Program Size (3.5.1.21361) | Reduction |
| ------------------------------ | -------------------------- | -------------------------- | --------- |
| Original Program               | 101,161                    | 101,161                    | /         |
| All Functions (134)            | 485,796                    | 360,548                    | 125,248   |
| Any 30 Functions               | 222,820                    | 180,428                    | 42,392    |
| Any 10 Functions               | 131,292                    | 114,068                    | 17,224    |

**Recommendation:**

The above data is for reference only. Version **3.5.1.21361** available, and users should evaluate based on their own program structure and selected functions to determine whether the optimized VBP version meets the **flash space** requirement.

#### Object‑File Merging Optimization Plan

**Optimization Strategy**

Since a static library (`.a`) contains multiple object files (`.o`), it is recommended to use the **object‑file merging** feature. This feature has been optimized as follows:

* **Before Optimization (Version 3.5.1.21385):**

  The merging process protected each `.o` file first and then merged them. As a result, after **virtualization**, each `.o` file contained its own VM instance. This caused a significant increase in file size because multiple identical VMs were embedded across different object files.
* **After Optimization (Version 3.5.1.21416):**

  The merging process now merges all `.o` files **before** applying protection. This means the merged `.o` file is virtualized only once, resulting in **a single VM instance**. Reducing the number of VMs directly decreases the overall file size impact.

**Comparison Before and After Optimization**

**Test Procedure:**

Using the pre‑optimization version (3.5.1.21385) and the post‑optimization version (3.5.1.21416), code virtualization was applied to a sample static library (`test_app.a`) with the **merge object files** option enabled. The file size changes were measured when virtualizing all functions and when virtualizing only selected functions.

**Reference Data:**

<br>

| **Virtualization Count** (3.5.1.21416) | Program Size (Before **Object‑File Merging**) | Program Size (After Merging) | Reduction |
| -------------------------------------- | --------------------------------------------- | ---------------------------- | --------- |
| Original Program                       | 373,982                                       | 373,982                      | /         |
| All Functions (97 functions)           | 1,089,224                                     | 731,644                      | 357,580   |
| Any 30 Functions                       | 541,392                                       | 443,308                      | 98,084    |
| Any 10 Functions                       | 400,248                                       | 302,444                      | 97,804    |

#### Reference Data (Version 3.5.1.21385)

| **Virtualization Count** (3.5.1.21385) | Program Size (Before **Object‑File Merging**) | Program Size (After Merging) | Reduction |
| -------------------------------------- | --------------------------------------------- | ---------------------------- | --------- |
| Original Program                       | 373,982                                       | 373,982                      | /         |
| All Functions (298 functions)          | 1,276,584                                     | 1,229,884                    | 46,700    |
| Any 30 Functions                       | 443,312                                       | 396,612                      | 46,700    |
| Any 10 Functions                       | 391,016                                       | 344,300                      | 46,716    |

Compared with version 3.5.1.21385, version 3.5.1.21416 shows a larger size increase when **object‑file merging is disabled** and **virtualization** is applied. This is because the new version optimizes the previous issue where functions were split into multiple branch entries. Although the total number of functions is reduced, each function body becomes larger. As a result, when merging is not enabled, the size increase is more noticeable than before.

**Recommendation:**

The above data is for reference only. Version **3.5.1.21416** is available, and users should evaluate based on their own program structure and selected functions to determine whether the optimized VBP version meets the **flash space** requirement.

### Comments

Since the test program cannot fully represent the customer’s real application scenarios, the test results may differ from actual usage conditions. The above results are for reference only. It is recommended to rely on the results obtained from real‑world usage and the actual **test environment** to make final assessments.
