AbstractThe increasing use of cloud computing and remote execution have made program security especially important.Code obfuscation has been proposed to make the understanding of programs more complicated to attackers. In thispaper, we exploit multi-core processing to substantially increase the complexity of programs, making reverse engi-neering more complicated. We propose a novel method that automatically partitions any serial thread into an arbitrarynumber of parallel threads, at the basic-block level. The method generates new control-ﬂow graphs, preserving theblocks’ serial successor relations and guaranteeing that one basic-block is active at a time using guards. The methodgenerates m n different combinations for m threads and n basic-blocks, signiﬁcantly complicating the execution state.We provide a correctness proof for the algorithm and implement the algorithm in the LLVM compilation framework.Keywords: Security, Obfuscation, Multi-threading.1. IntroductionWith the advent of cloud computing, software se-curity becomes especially important [1]. In particu-lar, software security researchers have been concernedto evaluate the methods that protect software systemsagainst reverse engineering threats. Those can be ex-ploitedbysoftwarehackersto discoverthe softwarevul-nerabilities and inject malicious code.One practical security approach is to use softwareobfuscation; it is a software security mechanism thattransforms the original program into a functionally-equivalent counterpart [2, 3]; the obfuscated programhas the same semantics as the original, but it is muchmore complex to understand by reverse engineers [4].Parallelism and multi-threading have been proposedto increase performance. Parallel programs are notori-ously difﬁcult to debug and reason about, and for thatreason parallelism is a nice ingredient for obfuscation.
The transformation includes methods such as insertion of dead or irrelevant code, extension to loop conditions, and conversion of a reducible into a non-reducible flow graph. One of the most important methods to do this is to increase parallelism of the code by two methods. First, redundant non-profitable task can be created then a portion of code is paralleled with this task, so the hacker cannot deduce which thread will have the actual portion to be run. Second, a sequence of data dependent statements in a portion of code can be splitted, then they run in parallel. The control for the correct execution will be using synchronization primitives @cite_2 . The latter is the closest to our method, however it splits simple, in-order sequence of instructions (without control-flow dependence) rather than general, complex, control-flow graphs as per our method.
@cite_2
2135947393 2116672403 2160248455 1889773849
Hardware and compiler techniques for mapping data-parallel programs with divergent control flow to SIMD architectures have recently enabled the emergence of new GPGPU programming models such as CUDA, OpenCL, and DirectX Compute. The impact of branch divergence can be quite different depending upon whether the program's control flow is structured or unstructured. In this paper, we show that unstructured control flow occurs frequently in applications and can lead to significant code expansion when executed using existing approaches for handling branch divergence. This paper proposes a new technique for automatically mapping arbitrary control flow onto SIMD processors that relies on a concept of a Thread Frontier, which is a bounded region of the program containing all threads that have branched away from the current warp. This technique is evaluated on a GPU emulator configured to model i) a commodity GPU (Intel Sandybridge), and ii) custom hardware support not realized in current GPU architectures. It is shown that this new technique performs identically to the best existing method for structured control flow, and re-converges at the earliest possible point when executing unstructured control flow. This leads to i) between 1.5 - 633.2 reductions in dynamic instruction counts for several real applications, ii) simplification of the compilation process, and iii) ability to efficiently add high level unstructured programming constructs (e.g., exceptions) to existing data-parallel languages. This paper presents the results of our investigation of code positioning techniques using execution profile data as input into the compilation process. The primary objective of the positioning is to reduce the overhead of the instruction memory hierarchy. After initial investigation in the literature, we decided to implement two prototypes for the Hewlett-Packard Precision Architecture (PA-RISC). The first, built on top of the linker, positions code based on whole procedures. This prototype has the ability to move procedures into an order that is determined by a “closest is best” strategy. The second prototype, built on top of an existing optimizer package, positions code based on basic blocks within procedures. Groups of basic blocks that would be better as straight-line sequences are identified as chains . These chains are then ordered according to branch heuristics. Code that is never executed during the data collection runs can be physically separated from the primary code of a procedure by a technique we devised called procedure splitting . The algorithms we implemented are described through examples in this paper. The performance improvements from our work are also summarized in various tables and charts. While the reconstruction of the control-flow graph of a binary has received wide attention, the challenge of categorizing code into defect-free and possibly incorrect remains a challenge for current static analyses. We present the intermediate language RREIL and a corresponding analysis framework that is able to infer precise numeric information on variables without resorting to an expensive analysis at the bit-level. Specifically, we propose a hierarchy of three interfaces to abstract domains, namely for inferring memory layout, bit-level information and numeric information. Our framework can be easily enriched with new abstract domains at each level. We demonstrate the extensibility of our framework by detailing a novel acceleration technique (a so-called widening) as an abstract domain that helps to find precise fix points of loops. According to a folk theorem, every program can be transformed into a program that produces the same output and only has one loop. We generalize this to a form where the resulting program has one loop and no other branches than the one associated with the loop control. For this branch, branch prediction is easy even for a static branch predictor. If the original program is of length κ, measured in the number of assembly-language instructions, and runs in t(n) time for an input of size n, the transformed program is of length O(κ) and runs in O(κt(n)) time. Normally sorting programs are short, but still κ may be too large for practical purposes. Therefore, we provide more efficient hand-tailored heapsort and mergesort programs. Our programs retain most features of the original programs--e.g. they perform the same number of element comparisons--and they induce O(1) branch mispredictions. On computers where branch mispredictions were expensive, some of our programs were, for integer data and small instances, faster than the counterparts in the GNU implementation of the C++ standard library.