Instruction/data protection employing derived obscuring instruction/data7000119Abstract A method and apparatus are described for protecting critical computer software and/or data with a large amount of obscuring instructions and or data to the extent that observing and understanding the obscured instructions and/or data is not humanly feasible. In a preferred method of obscuring software, a bank of obscuring instructions is prepared, a large number of obscuring instructions are selected from the bank and injected in the software code to be protected and a static image of the obscured sequence of code is encrypted and/or compressed. At execution, the obscured instructions are executed one at a time to make run time tracing a labor intensive process. Claims The invention claimed is: Description BACKGROUND OF THE INVENTION
The objective here is to obscure the assignment operation V1=1024, which is simple and straight forward and will not take more than a few seconds for a person skilled at the art to understand. However, by simply injecting some obscuring code, the obscured code will be significantly more time consuming to read and comprehend. The obscuring code selected may include a number of assignment instructions and some simple calculation instructions.
With the added obscuring instructions, it is still possible to isolate V1 out of V2, V3, and V4. However, now it will take a few minutes before a skilled person can read and identify the original critical instruction "V1=1024". The complexity of this example increases enormously when the original critical instructions are composed of 10 to 20 lines of instructions, these instructions are mixed with obscuring instructions on the order of millions including both instructions that are similar to the instructions to be protected and instructions that are dissimilar, and the critical instructions are randomly spread across many blocks. In fact, it becomes so time consuming to read and comprehend the obscured output that it will become humanly impossible. For example, consider the difficulties involved in interpreting a sequence of code if, instead of four values V1, V2, V3 and V4, the code included 100,000 values, returned all 100,000 values and there was no indication of which value or values had any significance. FIG. 2 depicts an obscuring code generator 203 and two predefined code banks: an obscuring code bank 204 and a transformation function bank 205. In FIG. 2, obscuring code generator 203 generates obscuring code blocks 206 that are used to protect the critical function source code 101. Illustratively, the available storage size is one million lines of code, the size of each block 206 is 125 lines of code, and there are 8000 blocks of code. Obscuring code generator 203 uses the critical function profile 106, security strength 103, storage size 201, and execution time 202 as input parameters for generating obscuring code from the two predefined code banks. Obscuring code bank 204 is a database that contains program instructions previously created through a manual, automated, or a combination of manual and automated process. Associated with each instruction is a unique numeric code. Thus, each numeric code identifies an obscuring instruction and the entire set of numeric codes identifies the entire set of obscuring instructions. Advantageously, each numeric code may simply be the memory address at which the instruction is stored in the obscuring code bank. Each code block 206 is a subset of the obscuring instructions available in obscuring code bank 204 and the instructions in block 206 can be identified by the numeric codes associated with those instructions. The program instructions in bank 204 comprise a large pool of instructions that are often built upon expertise and experience of the database designers. A large number of them resemble the most frequently used instructions in commonly used programming languages although a significant portion of them are purely random code without predefined profile. As indicated in the example given above, obscuring code may perform a function (e.g., returning the value V1=1024) that is useful to the critical function code to be protected but do so in a way that is very inefficient. Indeed, it may be spectacularly inefficient. Alternatively, as suggested by operations such V4=V4-V4, the code may be functional but may do nothing more than perform an operation and later perform the inverse of the operation so as to produce no effect other than obscuring the code to be protected. Other examples of obscuring code may have nothing do with the operation of the code to be protected but will still have to be deciphered because an attacker will not know which code is relevant and which is not. The presence of the obscuring instructions injected into the critical function source code has a direct impact on any attacker's ability to understand or modify the correct instructions in order to compromise the system's integrity. As an example, a segment of 10 lines of instructions that performs certain essential functions of a software application is serialized, distributed into multiple code blocks and mixed with 1,000,000 lines of obscuring code instructions. Using modern day microcomputers, one can assume that the typical CPU clock speed is beyond 500 MHZ. At such speeds, it takes a microcomputer no more than 8 milliseconds to execute all 1,000,000+ instruction provided the instructions perform relatively simply calculations, assignments and minimal I/Os. While the computational overhead is relatively low, the job for an attacker to understand and modify the key parts of these instructions is an insurmountable task. Even though the task of obtaining and observing the instructions is already difficult, let's assume an attacker can capture all 1,000,000 plus instructions and can observe and analyze them. This number of instructions amounts to 16,667 pages of printout on regular letter sized paper. Assuming the attacker can read at the speed of 3 minutes per page and work for an average of 8 hours a day, it will take him over 100 days just to finish reading the content in order to reach a shallow understanding of the instructions. As a practical matter, careful examination and much more time will ordinarily be required to identify the original 10 lines of critical instruction out of the 1,000,000 obscuring instructions. Furthermore, if a certain set of instructions is of even higher importance in a software application, more obscuring instructions can be injected. Consider the example of injecting 10,000,000 obscuring instructions. A modern computer can process these instructions within 80 milliseconds. However, the attacker will be challenged with a total of 166,667 pages of printout, and over three years just to finish reading them casually. The challenge is practically equivalent to finding a small needle in the Atlantic Ocean, which is humanly impossible to do. Current and future advances in computer microprocessor are rapidly accelerating. Today, CPUs that work at one GHz have been announced and CPUs that work at over 500 MHZ are commonplace. The faster a CPU can process instructions, the more obscuring instructions can be injected to protect critical functions of applications, and consequently, the harder it becomes for attackers to understand, identify or modify the protected instructions. Additionally, there are no currently known pattern recognition algorithms that can automatically parse, understand and locate critical instructions found in a large number of obscured instructions. Due to the largely random nature of the obscuring instructions, the pattern recognition task can be highly difficult. By adding more elaborate transformations and slicing of original code in combination with the large number of injected obscuring code, development of a pattern recognition algorithm can be made even more difficult. It is reasonable to assume the computational complexity of such an algorithm is at least as high as O(N3), where N is the number of instructions, with the possibility of being even as high as O(eNlogN). In the case of 10,000,000 ore more instructions, using the O(N3) estimate, one can expect the computation can take as long as 250,000 years on a 500 MHz CPU modern day personal computer. On the other end of the scale, it is evident that substantial protection can be achieved using far fewer obscuring instructions than 1,000,000. Even 10,000 lines of obscuring instructions represent a day's effort to read and typically much more time to understand. How much more time is a function of the intricacy of the code. As a practical matter with appropriate obscuring instructions, we believe it is reasonable to assume that it would take several months' effort to reach sufficient understanding of 10,000 such instructions to be able to identify and understand the operation of critical instructions embedded in such obscuring instructions. In some applications, several months' time is enough protection. As will be apparent, greater amounts of protection can be achieved with increasing numbers of lines of obscuring instructions. With 100,000 lines of obscuring instructions, we estimate the amount of time for one individual to reach an understanding of the operation of the critical instructions to be several years, which is often the length of time that a software product enjoys commercial success. In such circumstances, 100,000 lines of obscuring instructions may be enough protection. The transformation function bank 205 is a database previously created to contain mathematical functions that are one-to-one mappings from Set A to Set B and their inverse functions that are one-to-one mappings from Set B to Set A. Associated with each transformation is a unique numeric code. Thus, each numeric code identifies a transformation and the entire set of numeric codes identifies the entire set of transformations. Advantageously, each numeric code may simply be the memory address at which the instruction is stored in the transformation code bank. Preferably, Sets A and B are sets of numeric codes and the transformation T satisfies the following relationships: B=T(A) and A=T′(B) where T′ is the mathematical inverse function of T. Examples of T might be increment the value of A by 10 or multiply the value of A by 3; and the corresponding inverse functions would be decrement the value of B by 10 and divide the value of B by 3. Obscuring code generator 203 applies the transformations obtained from the transformation function bank 205 to the numeric codes associated with the obscuring instructions obtained from the obscuring code bank 204 to produce more obscuring instructions. In particular, generator 203 produces blocks of obscuring code 206. The first of these blocks is generated by generator 203 by selecting obscuring instructions from code bank 204. Additional blocks are generated by selecting transformations 210 from transformation function bank 205, applying these transformations to the numeric codes associated with the obscuring instructions found in a previously generated block of obscuring instructions so as to generate a set of transformed numeric codes and forming new blocks 206 of obscuring instructions using the instructions identified by the transformed numeric codes. Preferably, the transformations are selected randomly. The selected transformation functions are represented by elements 208 in FIG. 2 and their inverses by elements 211. In the event a transformation generates a numeric code that is outside the range of numeric codes, the generated numeric code "wraps around" as in modulus arithmetic so as to generate a numeric code that is within range. In the embodiment of the invention shown in FIG. 2 the transformation are concatenated so that any block CN is generated by a series of mathematical transformation T such that CN=TN(TN-1( . . . (T3(T2(C1))) . . . )), where C1 is an initial block of obscuring instructions selected from the obscuring code bank. By concatenating the transformations, it is possible to generate an enormous number of different transformations while storing only relatively few transformations in the transformation function bank 205. Alternatively, each block can be generated from the first block of obscuring instructions using a single transformation function instead of the concatenated set of functions. The use of transformations to generate additional blocks of obscuring instructions makes it possible to generate enormous numbers of additional obscuring instructions while allowing the system to compress and encrypt these instructions. To someone trying to understand the instructions, a block of instructions generated by a transformation of associated numeric codes can be every bit as difficult to understand as the original block of instructions. However, the transformed block can be represented simply by the transformation which can be represented by its numeric address in the transformation code bank. Thus, while it would require 125 numeric codes associated with instructions in code bank 204 to represent a first block of 125 obscuring instructions, a single numeric code associated with a transformation in function bank 205 can be used to generate from the first code block another 125 numeric codes associated with instructions in code bank 204 to represent a second block of 125 obscuring instructions and so on for additional blocks of obscuring instructions. Moreover, if the correspondences between the numeric codes and the obscuring instructions and the numeric codes and the transformation functions can be kept secret, the instructions may also be encrypted. It should be noted, however, that there are also computational costs involved in generating the additional blocks of code using the transformations. As a result, a typical practice is for generator 203 to produce several different blocks of obscuring code by selecting instructions form code bank 204 and then generate from each of these blocks of obscuring code several additional blocks of obscuring instructions by selecting transformations 210 from the transformation function bank 205. The composition of the first code block, the number of code blocks, the size of each code block, the number of obscuring instructions per line of code to be obscured, and the compression ratio to be maintained are determined by generator 203 from the critical function profile 106, security strength 103, storage size 201 and execution time 202. FIG. 3 depicts an obscuring code injector 301, run time apparatus 302 and an obscuration compiler 308. In FIG. 3, obscuring code injector 301 combines the serialized code blocks 104 and obscuring code blocks 206 with run time apparatus 302 to create a pre-compilation obscured image 307. Obscuration compiler 308 uses the pre-compilation program image 307 as input to create an obscured object level image 312. Run time apparatus 302 comprises the necessary programming instructions to load blocks of machine level code into a computer's memory for execution and to transfer execution control from one code block to another. A code locator 303 locates one or more blocks of programming instruction from a data file that will be described later in FIG. 6. A decryptor 304 decrypts the code block located by the code locator 303 into plain text machine level code in preparation for execution. A code loader 305 loads the decrypted code block into memory and starts the actual execution of the instructions. A control handler 306 hands over control of execution from the current code block to the next one in queue as soon as the current code block's execution is finished. Obscuring code injector 301 injects the run time apparatus 302 comprising elements 303, 304, 305, 306 into the serialized critical function source code blocks 104 and the obscuring code blocks 206 to minimize the possibility for the serialized critical function source code to be observed. As a result, image 307 comprises multiple collections of blocks 302, 104, and 206. At this stage, the pre-compilation obscured image 307 is ready to be compiled into object code. The obstruction compiler 308 is applied to pre-compilation obscured image 307 to create object level code blocks 309, 310, 311 in correspondence to blocks 302, 104 and 206. Each collections of a block 309, block 310, and 311 is referred to as an object level block Oi 312. Obscuration compiler 308 is a special purpose apparatus that augments a regular compiler by preserving the transformation constraints. With a regular compiler, the transformation function T for adjacent obscuring code blocks 206 would be lost once the source code is compiled into object level code using a regular compiler. However, obscuration compiler 308 implements the processing logic to preserve such transformation function even after object level code is created for the source code. Specifically, if the functional constraint exists between block C1 206 and block C2 206 and can be defined as follows (same as in FIG. 2): C2=T2(C1) and C1=T2′(C2) where T2 and T2′ are transformation functions and inverse transformation functions for C1 and C2, then, the obscuration compiler ensures that the corresponding object level code blocks OC1 311 and OC2 311 satisfy the following constraints: OC2=T2(OC1) and OC1=T2′(OC2) The implementation of a compiler that preserves the transformation information in this way will be known to those skilled in the art. By so preserving the transformation information, then transformation functions can be applied to the object level code to achieve compression if desired. In FIG. 4, an encryption processor 401 takes object level blocks 312 as input, and encrypts them in a recursive chain fashion. The encryption process is applied to all object level code blocks 312 starting with ON which includes blocks OLN 309, OEN 310, and OCN 311 and ending with O2 which includes blocks OL2 309, OE2 310, and OC2 311. The process is not applied to object code block O1 which includes blocks OL1, OE1, and OC1 The output of each stage i of encryption processor 401 is Di 402. The output of each stage except stage 2 is applied as an input to the encryption processor of the next stage. In general, each encryption processor Pi scrambles and thereby encrypts object level code block Oi 312 and the output of Di+1 of the previous processor in accordance with an algorithm specified by a key. Advantageously, a different scrambling algorithm is used for each encryption processor Pi and the key that specifies the algorithm is inserted in clear text in the output Di. This encryption process ensures that the output data file is encrypted and can not be directly de-compiled statically. Because the blocks are encrypted as they are compressed, attackers cannot directly decompile the data files to obtain the entire obscuring and critical function instructions. These characteristics force attackers to trace the execution of the system in this invention during run time as the only feasible means to observe the obscuring instructions. FIG. 5 illustrates the data file constructed at the end of the obscuring process. The final data file contains essentially OL1 309, OE1 310, OC1 311 and D2. D2 in turn contains the scrambled form of D3, and D3 contains D4, so on and so forth. Code blocks obscured in this fashion are protected against any direct de-compilation or disassembling attempts, because the contents of the data file are no longer recognizable for utilities that do not understand the specific format and de-compression process. Because the number of obscuring instructions can be in the millions, it is also desirable to incorporate data compression technology in the encryption processors 401. In the case where blocks of obscuring code are generated by mathematical transformations from a first block of obscuring code generated from obscuring code bank 204, substantial compression can be achieved simply by representing each block in terms of the first block of obscuring code and the numerical codes representing the transformations used to generate the block. Since obstruction compiler 308 ensures that the object level code that represents the obscuring instructions is related by just such a series of transformations, such compression is achievable by replacing the object level code blocks OCi 311 with the transformations that are used to generate these blocks. In addition, the numeric codes that identify these transformations can readily be scrambled and thereby encrypted at the same time as the encryption processor scrambles and encrypts object level code blocks OLi 309 and OEi 310. In particular, in a preferred embodiment of the invention that both encrypts and compresses the object level code, encryption processor PN 401 scrambles OLN 309 and OEN 310 and the numeric code representing transformation TN in accordance with an algorithm specified by a key N. These scrambled values and the clear text value of the key N constitute output DN 402. Compression is achieved by representing OCN 311 in terms of the scrambled numeric code representing transformation TN. Subsequently, DN 402, a numeric code representing transformation TN-1, a key N-1 and the next set of object level code blocks ON-1 are used as inputs for encryption processor PN-1 401. At this step, encryption processor PN-1 401 scrambles OLN-1 309, OEN-1 310 and DN 402, and a numeric code representing transformation TN-1 1. These scrambled values and the clear text value of key N-1 constitute output DN-1 402. Again, compression is achieved by representing OCN-1 311 in terms of the scrambled numeric code representing transformation TN-1. The compression and scrambling process continues for all the object level code blocks in 312 in the same fashion for sequence number N-2, N-3, N-4 . . . except for OL1 309, OE1 310, and OC1 311. Once the process is complete, OL1 309 can retrieve key2 so as to de-scramble D2402 at runtime to retrieve T2, OL2 309, OE2 311 and D3; and OC2 311 can be recreated using OC1 311 and the unscrambled numeric code for transformation T2. However, until D2 is descrambled and OL2 is executed, D3 remains undistinguishable. And similarly, until D3 is descrambled and OL3 is executed, D4 remains undistinguishable; and so on. Such constructs ensure that all the code blocks are only observable when the scrambled data blocks OL1 D2, D3, . . . , DN-1 and DN are scrambled and executed at runtime. FIG. 6 depicts a typical microcomputer system architecture and the execution model of the run time apparatus of the present invention. A data file 602 is stored on the computer's hard disk 601. The data file is loaded into the computer's real memory 603 at run time through the computer's main bus system 604. The actual memory space required to execute the code blocks contained in the data file is allocated separately and is illustrated at 605. FIG. 7 illustrates how the run time execution process starts with the first set of code blocks being loaded into memory and executed. All executions of the code blocks are conducted within the memory address space indicated as 605. As the first step, OL1 701 is loaded at memory address L1 711. This address will remain unchanged for all future OL2, . . . , OLN-1, OLN code blocks. CodeLoader 702 of OL1 executes within this space to allocate a dynamic memory location at address E1, 710. It is important that address E1, 710 be dynamically assigned to ensure that the execution process of the code blocks cannot be automatically traced at a fixed address using conventional or commercially available tools. Because of the dynamic nature of this address allocation, address E2 for the next set pf code blocks OL2 cannot be determined until the active instructions of OL1 descramble OL2. At address E1, the runtime image of a series of code blocks is loaded and executed, including OE1 704, OC1 705, Get(key2) 706, Decrypt (OL2) 707, Load (OL2) 708, and "Jump To Address L" 709. OE1 704 and OC1 705 are the mixed instruction blocks that contain both the original instructions in critical function source code 101 and the obscuring instructions for the first code block. The execution of instruction blocks 704 and 705 is the most essential action at this stage. Get(key2) 706 is the instructions that retrieves the encryption key2 so that OL2 can be decrypted and loaded into Address L. Decrypt(OL2) 707 is the set of instructions that actually decrypts and creates the executable OL2 code blocks. "Load (OL2) at Address L" 708 loads the decrypted OL2 instructions into the static memory address L 711 ready for the next step of processing. "Jump to L" 709 hands the control of execution to the instructions loaded at address L 711. At this stage, the essential functionality has been completed for step one and the system is ready to load and execute the next set of code blocks OL2 309, OE2 310, and OC2 311. In FIG. 8, OL2 801 has been decrypted and loaded into the static memory address L 711. A code locator 802 locates the compressed and scrambled code for OE2 807 and OC2 808 from the .DAT data file cache 606 and retrieves them for decryption and decompression. Decryptor 803 executes the actual decryption and decompression of the retrieved code blocks. A code loader 804 dynamically determines a memory address E2 806, allocates the necessary space, and loads the decrypted code blocks into it for execution. A control handler 805 transfers control of execution to the instructions loaded at E2 806. OE2 807 and OC2 808 contain the true instructions within the original critical function source code 101 and the obscuring code blocks. They are first executed as the most essential functionality of this step. Subsequent code segments 809, 810, and 811 are similar to the apparatus described in FIG. 7, and are executed to retrieve OL3, load OL3 into the static memory space at address L, then transfer execution control over to the instructions in that memory space. The execution of subsequent sets of code blocks, OL4 309, OE4 310, C4 311, OL5 309, OE5 310, C5 311, . . . , follow the same process as described above until all code blocks are loaded in memory and executed. Because the run time apparatus in this invention allows dynamic loading and execution of the blocks in data file, virtually any arbitrary number of obscuring instructions can be executed as long as execution overhead limit permits. Furthermore, because every block of instruction is executed at a dynamically assigned memory address, it makes tracing execution of these blocks a challenging task. Without highly specialized hardware devices, locating the address where a block of instructions is loaded in memory is virtually impossible. These characteristics of the runtime system ensure that obtaining and observing instructions in memory using tracing techniques are laborious and time consuming to the extent of being humanly impossible without the support of highly expensive and special designed hardware devices. The method and system described in the present invention can be applied to any digital material that includes an executable component. Whenever a software application includes implementation of highly valuable technology or other trade secrets, respective programming instructions can take advantage of the obscuring capability of the current invention. For computer security related products, the present invention offers these products robust anti-trace and anti-decompilation protection for programming instructions that are most vulnerable and critical in the products. Additionally, an embodiment of the present invention can enable different sets of obscuring programming instructions to be injected for each different protected product, user desktop computer, or user identification. High performance back end server systems can be optimized to extract obscuring instructions from the obscuring code bank specifically and differently according to the input of a machine id, user id, or other uniquely identifying parameters. Such capability to prevent any potential compromise of security can be generically applied to other products, customers, or machines. It can ensure the same amount of computational resource is required to crack each product or machine across a product line or customer line. As indicated, in the same fashion as the invention is used to protect critical software, the invention may also be applied to the protection of critical data by hiding the critical data amid vast quantities of obscuring data generated from an obscuring data bank. Additional quantities of obscuring data may also be generated by transforming the obscuring data using a transformation function bank. The obscured data may likewise be encrypted and/or compressed either as part of the same process that encrypts and/or compresses the software or independently thereof. In conclusion, the present invention makes it possible to protect critical programming instructions and/or data by injecting a large amount of obscuring instructions and/or data to the extent that observing and understanding of the obscured instructions and/or data is not humanly feasible. The apparatus and system of the present invention facilitates compression of obscuring instruction and/or data and the runtime execution of the obscuring instructions and/or data so that neither direct de-compilation nor real time tracing of the obscuring instructions and/or data can be achieved without the use of extensive and expensive computing resources only affordable by large organizations over an extraordinary time span.
|
Same subclass Same class Consider this |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
