Solution Overview-Protection Scheme for ARM Thumb Object Files and Static Libraries
introduce highly secure protection approach to protect ARM Thumb Object file/Static lib
Summary
This document presents a customized protection scheme of Virbox Protector designed for ARM‑based embedded environments, specifically targeting the Thumb instruction set. and fully supporting static libraries and object files generated by the IAR compiler.
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 provides a comprehensive workflow for securing object files (.o) and static libraries (.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 Protectoruses 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 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:
Compile the Thumb‑instruction
.objfile using the Android NDK.Use the Virbox Protector to protect the
.objfile, selecting the target functions for code virtualization.Inspect the protection results of the Thumb instruction set in the protected
.objfile.Verify whether the protected program can be successfully linked and compiled, and confirm that the resulting executable runs correctly.
IAR Embedded Workbench Compilation
Test Procedure:
Compile the Thumb‑instruction
.objfile using the IAR Embedded Workbench CompilationUse the Virbox Protector to protect the
.objfile, selecting the target functions for code virtualization.Inspect the linking behavior of the protected
.objfile when generating the final executable.Inspect the protection results of the Thumb instruction set in the protected
.objfile.
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.

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

Decompiled project After Protection
Invocation Process
Locate the virtualized function. When the program executes the instruction
BL sub_D8E4, double‑clickingBL sub_D8E4saves the return address and jumps toB.W loc_13626.As shown in the figure:
Intermediate Jump
Double‑click
B.W loc_13626to continue the jump (an unconditional branch that does not affect the LR register) to the new location. TheB.Winstruction is used in Thumb‑2 mode.As shown in the figure:
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:
View the C‑style pseudocode generated after decompiling the function.
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:

Note:
The titles of the four columns in the table above are:
Process ID (PID) , Turnaround Time, Waiting Time, Response Time;
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:

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:
Compile Thumb‑instruction
.objfiles using the IAR and Green Hills toolchains.Use the Virbox Protector (debug version with symbols) to protect the
.objfile. Select the functions to be virtualized and configure the number of VM instances (e.g., 4 VMs) in the advanced options.Examine the behavior of the linked executable generated from the protected
.objfiles.Inspect the protection effect on the Thumb instruction set within the protected
.objfiles.
Verification of Protection
Note: The debug version of the Virbox Protector (with symbols) is intended only for testing the multi‑VM functionality. The protected
.objfiles 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:

Decompiled View After Protection
Locate the protected function. The program execution reaches the instruction

Double‑click to jump to
BL vm_init_0.

Click X to enter the
vm_init_0function and observe the differences in its instructions.Following the same steps, you can compare the behavior of the other VM instances.

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:
Drag the
.objfile into theVirbox ProtectorGUI tool.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.

Verification of Watermark setting
Use the
010 Editortool to inspect the watermark configured during protection. After parsing the static library or binary file, directly search for the corresponding Hex Bytes.Enter the configured watermark value in the search field. If the watermark is stored in the
.textcode section after protection, the configured watermark bytes can be located within the code.

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:

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:

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
.ofile first and then merged them. As a result, after virtualization, each.ofile 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
.ofiles before applying protection. This means the merged.ofile 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:
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.
Last updated