N-Version Obfuscation: Impeding Software
Tampering Replication with Program Diversity
Hui Xu∗† , Yangfan Zhou†‡ , Michael R. Lyu∗
∗

arXiv:1506.03032v1 [cs.CR] 8 Jun 2015

†

Department of Computer Science, The Chinese University of Hong Kong
MoE Key Laboratory of High Confidence Software Technologies (CUHK Sub-Lab)
‡ Department of Computer Science, Fudan University
According to a recent survey [16], existing software diversification approaches generally consider functionally equivalent programs, which can be effective against several kinds of
attacks such as return oriented programming. However, such
approaches with the functionally equivalence constraint cannot
meet our need to disable the replication of tampered software.
As a first attempt, we propose to deliver same featured,
but functionally nonequivalent software copies to different
machines. A major challenge towards this goal is which
part of a program can have such functionally nonequivalent
diversities. We formally define the problem as N-version
obfuscation, and provide a viable solution for the software
of client-server architecture, i.e., by integrating a message
authentication code (MAC) mechanism with functionally
nonequivalent SHA1 algorithms [1] to the original software,
it can be resistant to tampering replication. We further show
that many software integrity protection problems can be
reduced to our solution model. It is worth noting that Nversion obfuscation can be applied seamlessly to other existing
tampering-resistant approaches, and hence equipping them
with the replication-resistant property. Our analysis result
shows that the tampering complexity incurred by N-version
obfuscation increases almost linearly with the number of
functionally nonequivalent software versions.

Abstract—Tamper-resistance is a fundamental software security research area. Many approaches have been proposed to
thwart specific procedures of tampering, e.g., obfuscation and
self-checksumming. However, to our best knowledge, none of them
can achieve theoretically tamper-resistance. Our idea is to impede
the replication of tampering via program diversification, and thus
increasing the complexity to break the whole software system. To
this end, we propose to deliver same featured, but functionally
nonequivalent software copies to different machines. We formally
define the problem as N-version obfuscation, and provide a viable
means to solve the problem. Our evaluation result shows that the
time required for breaking a software system is linearly increased
with the number of software versions, which is O(n) complexity.

I.

I NTRODUCTION

Once software has been released, it faces many security
threats. For example, attackers may crack its license protection
mechanism to sell pirated software, or they may pack some
payload (e.g., advertisement) into the original software for
certain purposes. Nowadays, the repacked app with malicious
payload becomes even a major threat to mobile security
[32]. Such attacks generally involve reverse engineering
techniques to tamper the software. To protect software from
tampering, two major approaches have been proposed, i.e.,
obfuscation and self-checksumming. On one hand, software
can be protected with obfuscation approaches to deter attackers
from locating the target code spot. On the other hand,
software can embed self-checksumming code to detect whether
it has been tampered during execution. Current tamperresistant work mainly proposes such kinds of tricks to thwart
specific tampering tools or approaches, such as using a
loop with unsolved conjectures to thwart symbolic execution.
Nonetheless, once the trick is recognized, skillful attackers can
design hand-crafted tools to launch an attack. It seems safe to
conjecture that software cannot achieve theoretically tamperresistance without trusted hardware circuits [6].

The rest of this paper is organized as follows. We first
discuss the background in Section II. We then demonstrate
our approach in Section III, and evaluate its effectiveness in
Section IV. The related work is discussed in Section V. Finally,
Section VI concludes this paper.
II.

BACKGROUND

A. Threat Model and Assumption
Software delivered to end users is vulnerable to tampering.
Attackers may modify the original program execution logic for
a specific purpose, and then replicate the tampering on other
machines. The modification can be achieved in two ways:

A general software tampering objective is to enable
replicating the tampering on other machines. Intuitively, we
cannot guarantee a piece of software to be fully tamperresistant, but we can fail the execution of tampered software
on general machines, other than the attacker’s. Such an
idea, known as program diversification [12], is to prevent
widespread attacks by making intrusions much harder to
replicate. If the attacker wish to run the tampered software
on another machine, she has to work on it specifically. In this
way, we can cut off the contagion of tampering so as to control
the scope of potential damage.

Software Repack: Attackers can manipulate the software
executables directly, for example, they may remove the original
advertisement module embedded in a mobile app installation
file, and replace it with another one beneficial to themselves.
Dynamic Injection: Attackers may also dynamically inject a
piece of code into the program process, so as to manipulate the
loaded program during execution, e.g., using Linux ptrace
tool. Such an approach is widely adopted by virus and antivirus software, which injects either inspection code to monitor
the program execution or places a back door to control it.
1

Use one way function
In this work, we consider the hostile host model [25], which
is widely adopted by software tamper-resistance work, such
as [9, 14]. We assume that to launch such tampering attacks,
attackers can use malicious host to analyze the software, and
they can fully inspect the software execution step by step.

/*Original code:*/

/*After obfuscation:*/

if (

if (

)
foo();

)
foo();

(a) One way function

int b = getchar();

B. Limitation of Anti-reverse Engineering

//always true
if (
foo();
}
else {
junk();
}

Reverse engineering is a crucial technology for software
tampering. It involves a process that analyzes and manipulates
a software based on its executables, e.g., in an executable and
linkable format (ELF). Anti-reversing techniques impedes such
a process by adding tricks into the executables to fool the
analyzer. General reverse engineering on ELF files involves
two phases: a disassembly phase, and an analyzing phase.
The disassembly phase decodes the ELF binaries to assembly
code, which can be performed by some tools (e.g., IDA)
automatically. We can hardly impede the decoding because
eventually the processor has to be able to decode and execute
the file. But on the other hand, many tricks have been
proposed to obstruct the analyzing phase. We discuss the major
approaches and their limitations in what follows.

(b) Opaque constant

){

int x = 2000;
while ( x > 1 ) {
if ( x % 2 == 1 ) {
x = 3 * x + 1;
}
else x = x / 2;
if ( x == 1)
foo();
}
(c) Collatz Conjecture

Fig. 1. Demonstration of obfuscation approaches with different tricks. The
original code for Fig. 1(b) and Fig. 1(c) is foo();

static analysis. Therefore, adversaries may also execute the
code to obtain execution instruction traces [24] or debug the
code step by step to perform the analysis, which is known
as online reverse-engineering [11]. Such an analysis process
generally would not be affected much by obfuscation [30],
and adversaries can leverage a set of system monitoring tools
to monitor the execution outcome of a code block, which
facilitates the reverse-engineering process. Researchers have
suggested to set traps with anti-debugger code to hinder
debugging. For example, one may simply check the debug
register to detect if a debugger is present, or count the
execution time of a code block to detect if it has been paused,
and then penalize the debugger [13, 27]. Again, if the trick
of anti-debugger code is recognized, adversaries can suppress
the checking by patching the binaries, or simply switching to
another debugger.

There are two general ways to do reverse analysis, i.e.,
the offline approach and the online approach. The offline
approach does not execute the assembly code, but directly
analyzes it using reverse engineering tools such as IDA [2].
If a program has not been properly obfuscated, its control
flow graph (CFG) can be easily derived, which shows the
assembly code in blocks, and indicates their call relationships.
In this way, the complexity of analyzing the assembly code can
be simplified. CFG can provide great assistance for reverse
engineers to grasp the meaning of the low-level assembly
code which has little semantics. To protect programs from
being analyzed offline, a few obfuscation approaches have
been proposed [31]. The main idea of obfuscation is to add
some junk code into the original program, while the original
functionality of the program is still preserved. Several methods
have been proposed to achieve this. For example, one can use
opaque constant to add blocks of junk code that would never be
executed. Fig. 1(b) shows such an example. He may further
create an NP-hard problem using a bunch of such code as
discussed in [20]. Another method is to confuse the trigger
condition of one code block with one-way function, so that
the static analyzer cannot infer whether the code would be
executed or is junk code. Fig. 1(a) shows an example of
transferring an obvious condition into an opaque condition
with a hash function. Besides, unsolved conjectures have
been proposed to confuse the exit criteria of loops, e.g.,
Fig. 1(c) is an example of using Collatz Conjecture. Such
obfuscation techniques can increase great difficulties to general
static analysis tools for analyzing the CFG and grasping the
meaning of assembly codes. However, all the obfuscation
approaches have vulnerabilities. For example, opaque constant
is vulnerable to symbolic execution which implements a smart
constraint solver, and unsolved conjectures are vulnerable
to homemade tools which can recognize the patterns of
conjectures.

When deriving enough understanding about the code,
adversaries can manipulate the binaries by adding or deleting
some code according to a specific purpose while preserving
its ability of execution. A possible way to detect such code
patching is to use self-checksumming code. The basic idea is
to pre-calculate a value of relative address (i.e.,, the checksum),
and let the program fetch instructions during execution
according to such a value. If the checksum governed regions
has been tampered, the instruction would not be correct and
the program would likely to suspend [29]. Using overlapped
self-checksumming code can further increase the strength of
protection. However, it can be defeated by carefully detecting
and removing them [23] or exploring the vulnerabilities [29]
of execution environment.
In other words, there is still no overwhelming anti-reverse
engineering method, i.e., software can never be made fully
resistant to tampering without hardware protection.

III.

Even though many powerful offline analysis tools are
available off-the-shelf, pure offline analysis still suffers hard
limitations in detecting some anti-reversing protections, e.g.,
runtime code unpacking is widely used by malware to escape

O UR P ROPOSED A PPROACH

In this section, we introduce the N-version obfuscation
problem first, and then discuss a possible solution with its
application scenario in achieving tamper-resistance.
2

for i = 0; i < 80; i + + do
if 0 ≤ i ≤ 19 then
f ← (b AND c) OR ((NOT b) AND d);
k ← 0X5A827999;
end
if 20 ≤ i ≤ 39 then
f ← b XOR c XOR d;
k ← 0X6ED9EBA1;
end
if 40 ≤ i ≤ 59 then
f ← (b AND c) OR (b AND d) OR (c AND d);
k ← 0X8F1BBCDC;
end
if 60 ≤ i ≤ 79 then
f ← b XOR c XOR d;
k ← 0XCA62C1D6;
end
temp ← (a LEFTROTATE 5) + f + e + k + w[i];
e ← d;
d ← c;
c ← b LEFTROTATE 30 ;
b ← a;
a ← temp;
end

A. Problem Definition
We formally define the N-version obfuscation problem
as following: Given an algorithm A, how to automatically
generate a large set of functionally nonequivalent algorithms
{C1 , ...Cn }, which are similar to A, and their parent algorithm
P , so that they meet the following two properties:
Homo Property: when performing on the same task, P can
output the same result as Ci , if the gene vector {g1 , ...gn } of
Ci is known to P .
Divergence Property: when performing on the same task,
Ci and Cj generally output different results.
Suppose the software architecture is client-server mode,
we can deploy the parent algorithm on the server side, and
deliver a unique children algorithm to each client. In this way,
the software distributed to client can have such functionally
nonequivalent diversities according to the divergence property,
and the homo property enables the server to handle such
diversities.

Algorithm 1: The main loop of SHA1

B. N-Version Obfuscated SHA1
In this section, we show a viable means to solve the
N-version obfuscation problem with SHA1 algorithm. Our
approach leverages the iterations of calculations needed by
SHA1 to generate functionally nonequivalent diversities.

Data: f p[80], k[80], w[80]
for i = 0; i < 80; i + + do
Call f p[i];
// Pointer to F0, F1, F2 or F3
F TAIL(k[i], w[i]);
end
Function F0()
f ← (b AND c) OR ((NOT b) AND d);
Function F1()
f ← b XOR c XOR d;
Function F2()
f ← (b AND c) OR (b AND d) OR (c AND d);
Function F3()
f ← b XOR c XOR d;
Function F TAIL(k, w)
temp ← (a LEFTROTATE 5) + f + e + k + w;
e ← d;
d ← c;
c ← b LEFTROTATE 30;
b ← a;
a ← temp;

The main loop of original SHA1 (Algorithm 1) includes
80 rounds of iterations. Each iteration takes one plaintext
block (w[i]) into calculation. For every twenty rounds, the
calculation (the equation for generating f and the value of
k) switches to another one. Even though there are some
security considerations of choosing a specific calculation for
each round, no evidence shows the program would suffer
great security degradation if we switch them with each other.
Therefore, we can diversify the original SHA1 algorithm by
choosing different sequences of equations for generating f and
values of k, which are the genes of individuals. We can also
design a parent algorithm which can receive the genes of a
child, and process data input according to the setting of genes.
Algorithm 2 shows the such a parent algorithm we designed.
In Algorithm 2, the pointer array of equations (f p[80]) for
generating f , and the value array of k for the 80 rounds of
iterations are passed to the algorithm as the genes of a child.
It is clearly seen that, given the same input w[80], the parent
algorithm can compute the same result as a child when f p[80]
and k[80] are properly set.

Algorithm 2: A parent algorithm for SHA1

According to Algorithm 2, each gene (either f p[i] or k[i])
has four possibilities, so we use two bits to represent a gene.
In each compilation, we first randomly generate two 160
length bit sequences: one as the chromosome for the equation
function pointer (i.e., f p[80]) and the other as the chromosome
for the value option of k (i.e., k[80]). We then replace the
corresponding AST branches with hardcoded equation function
pointers and settings of k.

C. Implementation
We automate the process of generating N-version SHA1
algorithms based on Low Level Virtual Machine (LLVM) [4],
which is a widely used opensource compiler that supports
extensions. LLVM first represents the source code with
Abstract Syntax Tree (AST), and then transfers it into
intermediate code (IR), which would finally be compiled
into executables according to a specific platform. LLVM
supports plugin and libtooling, which can manipulate
the source code of a target AST branch during the compilation
process. We hence customize a libtooling tool that can
automatically generate the N-version obfuscation algorithms.

It is worth noting that the N-version obfuscation approach
itself does not provide any resistance to reverse engineering.
However, our approach can be seamlessly integrated with
other aiti-reverse engineering protections, for example, the
obfuscation approaches proposed in [20], which composes NPhard problems with function pointers and opaque constants.
3

Client 1

Server

Guard
MAC
SHA1
Child

MAC
SHA1
Parent

Client 2
Guard

Client N

N-Version
Database

Fig. 3. An exemplary Android app (com.sankuai.meituan), which
has been injected with an LBE library (com.lbe.../client.jar). By
checking the maps file of the app process (pid:3789), we can detect the
tampering.

Guard

Fig. 2.

Application of N-version obfuscated program in tamper resistance

Data: dict < segment >
// A list of predefined segment with name
and size
Function IntegrityChk()
pid ← getpid();
f ile ← open (/proc/pid/maps);
while line ← readline(f ile) != EOF do
segN ame ← GetSegName(line);
segSize ← GetSegSize(line);
if !dict.contains(segN mae) then
Reaction();
else
if dict.getsize(segN mae)!=segSize then
Reaction();
end
end
end

D. Application Discussion
Many client-server software can adopt the N-version
obfuscation idea by implementing a MAC mechanism with
the obfuscated algorithm (e.g., SHA1). MAC is a popular
mechanism adopted by client-server computing architecture
to check the integrity and authenticity of messages. When a
client sends a request to the server, it calculates the MAC of
the request and appends it to the original request. The server
validates the MAC first and then processes the request. Since
a hash function is one major component of a MAC algorithm,
the N-version obfuscated SHA1 algorithms can be adopted.
Fig. 2 illustrates such a mechanism.
In Fig. 2, each client is embedded with a unique SHA1based MAC calculation algorithm. To successfully perform a
request to the server, it has to send the identification (such
as machine serial number or user id), the request, and the
MAC together to the server. The server queries the genes
of a client from its local N-version database according to
the identification of the client, and then verifies the MAC.
The distribution of such diverse programs can be achieved by
implementing the MAC in mobile code (i.e., dynamic library),
and delivering it by the server upon request. In other words,
the client software can be launched without the library at the
first time and then requests the server for the library. The
server randomly chooses a library from a pool of pre-compiled
libraries and delivers it to the client; in the meanwhile, it
records the mapping between the genes of the client and its
unique identification in the N-version obfuscation database. In
this way, the server can verify the MAC generated by each
client according to the homo property.

Algorithm 3: An exemplary integrity checking function

with a previously defined standard dictionary by developers.
If there are abnormal segments in the maps, i.e., the integrity
has been violated, a responsive mechanism can be triggered.
Such an approach is effective in detecting either software
repacking or dynamic injection attack as we have discussed
in the adversary model. We show an exemplary App (Fig. 3),
which is tampered by LBE (a commercial security software
for Android [3]). Algorithm 3 can detect such a tampering
by finding that com.lbe.../client.jar is an abnormal
segment.
If an attacker has successfully tampered one copy of the
guard (e.g., removing the integrity checking function) and
replicated it on other machines, the server can detect the
replication because of an incorrect MAC, i.e., inconsistent
mapping between the identification and genes. We may further
implement a reaction mechanism to renew the guard or crash
the client software directly.

We hence provide tamper-resistant capability for the client
software based on the N-version obfuscation library, which is
resistant to replication according to the divergence property.
To this end, a viable means is to implement an integrity
checking function align with the MAC in the library, so that
the library can serve as a security guard for the software.
By interleaving the code of the integrity checking function
with the MAC algorithm, the integrity checking can be
triggered when calculating a MAC. Algorithm 3 shows such an
exemplary integrity checking function for the apps of Android
operating system. The function navigates the maps file of
the app process itself, which records the program segments
and their address in the memory. It then compares the record

A question to ask is why we do not simply use different
keys to compose diversities? For example, we may use a
keyed-hash message authentication code (HMAC) algorithm
and hardcode a unique symmetric key into each client library.
Note that such an approach is also effective, but it is more
vulnerable than ours, because hiding a key (i.e., whitebox
cryptography) is more difficult than hiding the program logic
[7].
4

IV.

E VALUATION

taint analysis [30], which is computational intensive and timeconsuming. Let c3 denote the time needed to extract the genes
of a guard. The time needed to tamper the software system can
be estimated to c0 + n ∗ (c1 + c3 ). Because c3 is not negligible,
the complexity still equals to O(n). Note that n can be made
arbitrally large as the obfuscation task can be fully automated.

The goal of our work is to impede tampering replication
by creating diverse software instances, and thus to increase the
tampering complexity to the software system. In this section,
we evaluate the complexity increased by N-version obfuscation
for tampering multiple software clients.

V.

Suppose a program has adopted the protection mechanism
discussed in Section III-D. A decent attacker wishes to
manipulate the program binaries for a specific purpose,
including adding, deleting and modifying. According to the
adversary analysis, the guard cannot be simply removed or
disabled from the app, because the MAC mechanism rested in
the guard needs to be executed. However, In a hostile host
environment, the software can be fully inspected. Through
careful analysis, the attacker can identify that the protection
lies in the integrity checking function, i.e., Algorithm 3. She
can disable the checking by carefully removing the function or
suppressing the reaction. If there is no N-version obfuscation
protection, the attacker can deliver the tampered software
to other users. The replication of tampered software can be
executed normally on other machines, and the whole software
system is broken. With N-version obfuscation protection, if
the attacker simply repacks the app with tampered guard,
the message verification would fail, and the program cannot
function normally. To break the whole software system, the
attacker need to analyze and tamper the guard of each machine
specifically.

R ELATED W ORK

In this section, we first review the important work in reverse
engineering field, and then discuss the work of applying
automated program diversity to improve program security.
A. Reverse Engineering
Software protection is a research problem since decades
ago. The proposed solutions are generally two-fold: the hardware circuit assisted solutions which provide better security
assurance, or the pure software solutions which have better
adaptability to general hardware [6]. For our research problem,
hardware circuit assisted solutions are not applicable because
of their requirement on specific hardware, so we mainly discuss
the pure software solutions.
Literatures on software protection with anti-reverse engineering approaches have different purposes. While some
researchers look for protections against piracy [17, 20]
and intrusion [5], others investigate on impeding malware
against detection [19, 21, 26]. However, they share a set of
common protection techniques with only slightly difference.
Obfuscation is a basic software protection approach. It can
complicate the binaries, and increases the difficulty of the
reverse engineering. Ogiso et al. propose to obfuscate the
code by constructing a NP-Hard complexity problem, that
requires to determine the real function pointer from an array
of pointers [20]. However, such an approach is vulnerable to
symbolic execution with constraint solvers. To thwart symbolic
executions, Sharif et al. notice that some code blocks can
be concealed by setting a trigger condition with an one-way
function, so that the constraint solver cannot solve [26]. Wang
et al. propose another obfuscation technique to combat the
symbolic execution by exploring the general limitation of
symbolic execution tools in analyzing loops. Their idea is
to use unsolved conjectures [28] to confuse the termination
condition of loops. The approach is vulnerable when the
tricks of unsolved conjectures are recognized. Other than
setting tricks on the source code, Linn et al. propose to
obfuscate the binaries directly by inserting some error bits,
which can be automatically corrected during execution by
the CPU but not by current disassembly tools [17]. The
security of such a protection is very limited and vulnerable
to dynamic analysis, i.e., the actual instruction trace can
be easily obtained once the software is being executed. To
deter dynamic analysis with debuggers, Oishi et al. propose
to use some camouflaged anti-debuggers, which, however,
is not effective for homemade debuggers. On protecting
software from tampering, another general popular approach
is to detect the unauthorized modifications during runtime
by employing a self-checksumming mechanism [5, 14]. The
self-checksumming mechanism uses redundantly overlapped
checksum testers inside the program to verify its integrity. On
the other side, several investigations focus on defeating the
protections [19, 23, 29, 30]. Wurster et al. propose to defeat the

In order to tamper the software on another machine, the
attacker has to obtain the guard on that machine, develop a
tampered guard, and then substitute the original one. If the
guard is protected with interleaved self-checksumming code
[5], a successful tampering requires removing all the selfchecksumming code at the same time, of which the chance is
very low without sophisticated analysis. Existing approaches
on identifying such code generally require using dynamic
analysis and taint analysis together [23]. Empirically, the time
required to tamper each guard is not negligible.
Let c0 denote the time needed for analyzing one software
copy and tampering it on the attacker’s own hostile host. The
time complexity is O(1), which is equal to tampering one
software copy without N-version obfuscation. Let c1 denote
the time needed to obtain the guard on another machine, so
as to replace it, and c2 denote the time needed to tamper it.
If the attacker wishes to tamper the software on n machines,
the total time can be estimated as c0 + n ∗ (c1 + c2 ). Because
of the interleaved self-checksumming code, c2 should not be
negligible, hence the complexity equals to O(n).
Another possible tampering approach is to build an
algorithm similar to the parent algorithm, which can calculate
the hash value according to the genes of a specific child. To
this end, the attacker has to recognize the gene setting of
each guard. She may compare the difference between two
implementations, and locate the genes. If the attacker has
derived enough knowledge about our N-version obfuscation
theory and implementation, such kind of attack is theoretically
possible. However, if the guard is obfuscated (e.g., with [20]),
it would be very difficult. To our best knowledge, existing work
on breaking such obfuscated programs either require symbolic
execution with sophisticated constraint solvers or complicated
5

self-checksumming approach with a duplicated memory attack,
and examine its effectiveness on several popular cpu types
[29]. Qiu et al. propose to identify the self-checksumming
code using taint analysis approaches [23]. Yadegari et al. find
a more general way of deobfuscating an obfuscated algorithm
[30]. Our work is different from all the existing work in that
we focus on impeding the replication of software tampering.
B. Automated Software Diversification
The idea of software diversity is initially proposed for
software reliability engineering [18]. Cohen in [8] firstly
proposes to create functionally equivalent programs to enhance
software security. Forrest et al. in [12] also states that the
beneficial effects of diversity in computing systems have
been overlooked, and introducing diversities into computer
systems can make them more robust to replicated attacks.
They propose several possible ways to create such diversities
with respect to the program behavior, including adding
nonfunctional code, refectoring code, or diversifying the
memory layout. Crane et al. in [10] build upon finegrained code diversification to prevent code-reuse attacks.
They adopt function permutation [15], register allocation
randomization, and callee-saved register save slot reordering
[22] in the diversification process. However, to our best
knowledge, all these work do not consider to automatically
generate functionally nonequivalent programs to improve
tamper-resistance.
VI.

R EFERENCES

[2]
[3]
[4]
[5]

[6]

[7]

[8]
[9]

S. Crane, C. Liebchen, A. Homescu, L. Davi, P. Larsen, A.-R. Sadeghi,
S. Brunthaler, and M. Franz. Readactor: Practical code randomization
resilient to memory disclosure. In IEEE Symposium on Security and
Privacy, S&P, volume 15, 2015.

[11]

E. Eilam. Reversing: secrets of reverse engineering. John Wiley &
Sons, 2011.

[12]

S. Forrest, A. Somayaji, and D. H. Ackley. Building diverse computer
systems. In Operating Systems, 1997., The Sixth Workshop on Hot
Topics in, pages 67–72. IEEE, 1997.

[13]

M. N. Gagnon, S. Taylor, and A. K. Ghosh. Software protection through
anti-debugging. Security & Privacy, IEEE, 5(3):82–84, 2007.

[14]

B. Horne, L. Matheson, C. Sheehan, and R. E. Tarjan. Dynamic selfchecking techniques for improved tamper resistance. In Security and
privacy in digital rights management, pages 141–159. Springer, 2002.

[15]

C. Kil, J. Jim, C. Bookholt, J. Xu, and P. Ning. Address space layout
permutation (aslp): Towards fine-grained randomization of commodity
software.
In Computer Security Applications Conference, 2006.
ACSAC’06. 22nd Annual, pages 339–348. IEEE, 2006.

[16]

P. Larsen, S. Brunthaler, and M. Franz. Automatic software diversity.
IEEE Security & Privacy, (2):30–37, 2015.

[17]

C. Linn and S. Debray. Obfuscation of executable code to improve
resistance to static disassembly. In Proceedings of the 10th ACM
conference on Computer and communications security, pages 290–299.
ACM, 2003.

[18]

M. R. Lyu et al. Handbook of software reliability engineering. IEEE
computer society press CA, 1996.

[19]

A. Moser, C. Kruegel, and E. Kirda. Limits of static analysis for
malware detection. In Computer security applications conference, 2007.
ACSAC 2007. Twenty-third annual, pages 421–430. IEEE, 2007.

[20]

T. Ogiso, Y. Sakabe, M. Soshi, and A. Miyaji. Software obfuscation on
a theoretical basis and its implementation. IEICE TRANSACTIONS on
Fundamentals of Electronics, Communications and Computer Sciences,
86(1):176–186, 2003.

[21]

P. O’Kane, S. Sezer, and K. McLaughlin. Obfuscation: The hidden
malware. Security & Privacy, IEEE, 9(5):41–47, 2011.

[22]

V. Pappas, M. Polychronakis, and A. D. Keromytis. Smashing the
gadgets: Hindering return-oriented programming using in-place code
randomization. In Security and Privacy (SP), 2012 IEEE Symposium
on, pages 601–615. IEEE, 2012.

[23]

J. Qiu, B. Yadegari, B. Johannesmeyer, S. Debray, and X. Su. A
framework for understanding dynamic anti-analysis defenses. In
Proceedings of the 4th Program Protection and Reverse Engineering
Workshop, page 2. ACM, 2014.

[24]

J. Qiu, B. Yadegari, B. Johannesmeyer, S. Debray, and X. Su.
Identifying and understanding self-checksumming defenses in software.
2015.

[25]

T. Sander and C. F. Tschudin. Protecting mobile agents against
malicious hosts. In Mobile agents and security, pages 44–60. Springer,
1998.

[26]

M. I. Sharif, A. Lanzi, J. T. Giffin, and W. Lee. Impeding malware
analysis using conditional code obfuscation. In NDSS, 2008.

[27]

T. Shields. Anti-debugging–a developers view, 2010.

[28]

Z. Wang, J. Ming, C. Jia, and D. Gao. Linear obfuscation to combat
symbolic execution. In Computer Security–ESORICS 2011, pages 210–
226. Springer, 2011.

[29]

G. Wurster, P. V. Oorschot, and A. Somayaji. A generic attack
on checksumming-based software tamper resistance. In Security and
Privacy, 2005 IEEE Symposium on, pages 127–138. IEEE, 2005.

[30]

B. Yadegari, B. Johannesmeyer, B. Whitely, and S. Debray. A generic
approach to automatic deobfuscation of executable code. Technical
report, 2015.

[31]

F. Zhang, D. Wu, P. Liu, and S. Zhu. Program logic based software
plagiarism detection. In Symposium on Software Reliability Engineering
(ISSRE), 2014.

[32]

Y. Zhou and X. Jiang. Dissecting android malware: Characterization
and evolution. In Security and Privacy (SP), 2012 IEEE Symposium
on, pages 95–109. IEEE, 2012.

C ONCLUSION

This work has investigated software tamper-resistant issues
with N-version obfuscation. We have formally defined the Nversion obfuscation problem and provided a viable solution
with SHA1 algorithm. We have further discussed the application of such an N-version obfuscation idea in the clientserver software architecture. By introducing such functionally
nonequivalent diversities, our approach is effective to impede
the replication of tampering. The evaluation result shows that
the complexity to tamper the software system is linearly
increased with the number of software versions, which can
be automatically generated with trivial cost.

[1]

[10]

FIPS Pub 180-4: Secure Hash Standard (SHS). http://csrc.nist.gov
/publications/fips/fips180-4/fips-180-4.pdf.
IDA. https://www.hex-rays.com/products/ida/.
LBE. http://www.lbesec.com.
LLVM. http://llvm.org/.
H. Chang and M. J. Atallah. Protecting software code by guards. In
Security and privacy in digital rights management, pages 160–175.
Springer, 2002.
Y. Chen, R. Venkatesan, M. Cary, R. Pang, S. Sinha, and M. H.
Jakubowski.
Oblivious hashing: A stealthy software integrity
verification primitive. In Information Hiding, pages 400–414. Springer,
2003.
S. Chow, P. Eisen, H. Johnson, and P. C. V. Oorschot. Whitebox cryptography and an aes implementation. In Selected Areas in
Cryptography, pages 250–270. Springer, 2003.
F. B. Cohen. Operating system protection through program evolution.
Computers & Security, 12(6):565–584, 1993.
C. S. Collberg and C. Thomborson. Watermarking, tamper-proofing,
and obfuscation-tools for software protection. Software Engineering,
IEEE Transactions on, 28(8):735–746, 2002.

6

