Code obfuscation method, business execution method, medium, equipment and product
By compiling the second language code into a compressed file and writing it as a constant in the first language code, and then using a secure compiler to obfuscate it, an executable file of mixed code is generated, which solves the protection problem of cross-language integrated code and achieves an effective security improvement for mixed code.
Patent Information
- Application Number
- CN202511378036.8
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2025-09-24
- Publication Date
- 2026-01-02
AI Technical Summary
Existing code obfuscation tools and techniques typically focus on a single language environment, lacking a unified and efficient means to systematically obfuscate the overall mixed code formed after cross-language integration, resulting in software products that are integrated through cross-language code not being effectively protected.
Obtain the first language code and the second language code to be mixed, compile and compress the second language code into a compressed file, write it as a constant in the first language code according to a preset encoding method, and perform obfuscation processing through a secure compiler to generate an executable file of the mixed code.
It effectively improves the security of hybrid code, resists reverse engineering attacks, and enhances the execution security of the code.
Smart Images

Figure CN121256757A_ABST
Abstract
Description
Technical Field
[0001] This specification relates to one or more embodiments in the field of computer technology, and more particularly to a code obfuscation method, a business execution method, a medium, an apparatus, and a product. Background Technology
[0002] As software functions become increasingly complex, a single programming language often cannot meet development needs, so cross-language code integration has emerged.
[0003] Cross-language code integration refers to the technical practice of integrating code modules written in different programming languages within the same software system and enabling them to work together. This integration approach allows developers to choose the most suitable programming language based on specific tasks (such as performance, library ecosystem, or domain characteristics), effectively overcoming the limitations of a single language and thus improving development efficiency while optimizing the overall performance of the system.
[0004] For software systems built through cross-language code integration, their core business logic and algorithm code are critical assets. Therefore, this code must be heavily protected to prevent attackers from obtaining the system's actual implementation details through reverse engineering techniques (such as decompilation and disassembly).
[0005] Currently, code obfuscation is a common technique for protecting software code from reverse engineering. Its core principle is to convert source code or intermediate representation (such as bytecode) into a functionally equivalent but formally complex and poorly readable version.
[0006] However, existing code obfuscation tools and techniques typically focus on single-language environments. Because different programming languages differ significantly in many aspects, such as runtime mechanisms and compilation, there is currently a lack of unified and efficient methods to systematically obfuscate the hybrid code resulting from cross-language integration. This means that software products obtained through cross-language code integration cannot be effectively protected. Summary of the Invention
[0007] In view of the above, one or more embodiments of this specification provide the following technical solutions:
[0008] According to a first aspect of one or more embodiments of this specification, a code obfuscation method is proposed, comprising:
[0009] Obtain the first language code and the second language code to be mixed. The first language code is used to provide the target service. When the target service is executed, the second language code is called by the first language code to realize the specified function required when executing the target service. The first language code is compiled into machine code and executed by the operating system on which the first language code is based. The second language code is compiled into bytecode and executed by the virtual machine corresponding to the second language code.
[0010] The second language code is compiled and compressed to obtain a compressed file, which contains the bytecode corresponding to the second language code;
[0011] According to a preset encoding method, the compressed file is encoded, and the string corresponding to the compressed file obtained after data encoding is written into the first language code as a constant to obtain mixed code;
[0012] The mixed code is obfuscated using a preset security compiler to obtain the executable file corresponding to the mixed code.
[0013] Optionally, the first language code includes C++ code or C code, the second language code includes Java code, and when the second language code is Java code, the virtual machine is a Java Virtual Machine, and the compressed file is a Jar file.
[0014] Optionally, the string corresponding to the compressed file obtained after data encoding is written into the first language code as a constant to obtain hybrid code, specifically including:
[0015] Compile the source file of the custom class loader to obtain the string corresponding to the custom class loader. The custom class loader is used to determine the storage location of the bytecode corresponding to each class contained in the Jar file when the executable file is executed, so that the Java Virtual Machine can obtain and execute the bytecode based on the storage location.
[0016] The strings corresponding to the custom class loader and the Jar file are written into the first language code as constants to obtain hybrid code.
[0017] According to a second aspect of one or more embodiments of this specification, a service execution method is provided, comprising:
[0018] An executable file is obtained by obfuscating the hybrid code using a preset security compiler. The hybrid code is obtained by writing a string obtained by encoding the compressed file according to a preset encoding method as a constant into the first language code. The compressed file is obtained by compiling the second language code and compressing and packaging it. The first language code is used to provide the target business. The second language code is called by the first language code to implement the specified function required when executing the target business. The first language code is compiled into machine code and executed by the operating system on which the first language code is based. The second language code is compiled into bytecode and executed by the virtual machine corresponding to the second language code.
[0019] When executing the target service by running the executable file, the executable file is loaded into a preset memory, and the virtual machine corresponding to the second language code is started;
[0020] The string in the executable file is decoded using the decoding method corresponding to the encoding method to obtain the compressed file;
[0021] The compressed file is parsed, and the storage location of the bytecode corresponding to the target class to be called is determined in the preset memory.
[0022] The virtual machine corresponding to the second language code retrieves the bytecode corresponding to the target class from the storage location and executes it to obtain the execution result.
[0023] Optionally, the first language code includes C++ code or C code, the second language code includes Java code, and when the second language code is Java code, the virtual machine is a Java Virtual Machine, and the compressed file is a Jar file;
[0024] Parsing the compressed file and determining the storage location of the bytecode corresponding to the target class to be called in the preset memory includes:
[0025] A custom class loader is loaded to parse the Jar file and to determine the mapping relationship between the target class and the storage location of the corresponding bytecode of the target class through the findclass method in the custom class loader. The mapping relationship is defined by overriding the findclass method.
[0026] Based on the mapping relationship, the storage location of the bytecode corresponding to the target class in the preset memory is determined.
[0027] Optionally, the mapping relationship can be defined by overriding the findclass method, specifically including:
[0028] The custom class loader parses the Jar file to traverse the entry names of all classes contained in the Jar file;
[0029] Based on the entry name, the bytecode corresponding to each class is decompressed and read from the Jar file in sequence, and the bytecode corresponding to each class is stored in a data table created in a preset memory.
[0030] The findclass method is rewritten according to the storage location of the bytecode corresponding to each class in the data table, so as to establish a mapping relationship between each class and the storage location of the bytecode corresponding to each class.
[0031] Optionally, the findclass method is rewritten according to the storage location of the bytecode corresponding to each class in the data table to establish a mapping relationship between each class and its corresponding bytecode, specifically including:
[0032] Convert the entry name of each class into a fully qualified class name;
[0033] According to the storage location of the bytecode corresponding to each class in the data table, the findclass method is rewritten to establish a mapping relationship between the fully qualified class name of each class and the storage location of the bytecode corresponding to each class;
[0034] Based on the mapping relationship, determining the storage location of the bytecode corresponding to the target class in the preset memory specifically includes:
[0035] Based on the fully qualified class name of the target class and the mapping relationship, determine the storage location of the bytecode corresponding to the target class in the preset memory.
[0036] According to a third aspect of one or more embodiments of this specification, an electronic device is provided, comprising: a processor; a memory for storing processor-executable instructions; wherein the processor implements the steps of the code obfuscation method described above by executing the executable instructions.
[0037] According to a fourth aspect of one or more embodiments of this specification, a computer-readable storage medium is provided that stores computer instructions thereon, which, when executed by a processor, implement the steps of the code obfuscation method described above.
[0038] According to a fifth aspect of one or more embodiments of this specification, a computer program product is provided, comprising a computer program / instructions that, when executed by a processor, implement the steps of the code obfuscation method described above.
[0039] As can be seen from the above embodiments, firstly, the first language code and the second language code to be mixed are obtained. Then, the second language code is compiled and compressed to obtain a compressed file. The compressed file is encoded according to a preset encoding method, and the string corresponding to the compressed file obtained after data encoding is written into the first language code as a constant to obtain the mixed code. Finally, the mixed code is obfuscated by a preset security compiler to obtain the executable file corresponding to the mixed code.
[0040] As can be seen from the above method, by writing the second language code as a constant into the first language code and using a secure compiler to perform overall obfuscation on the resulting hybrid code, the final executable file can effectively resist reverse engineering attacks, thereby effectively improving the overall security of the hybrid code. Attached Figure Description
[0041] Figure 1 This is a schematic diagram illustrating the steps involved in the code obfuscation method provided in this manual;
[0042] Figure 2 This document provides a schematic diagram illustrating the steps involved in the business execution method described herein.
[0043] Figure 3 This specification provides a schematic diagram illustrating the generation of an executable file through obfuscation.
[0044] Figure 4 This is a diagram illustrating the execution of an executable file generated based on obfuscation methods, as provided in this specification.
[0045] Figure 5 This is a schematic structural diagram of a device provided in this manual;
[0046] Figure 6 A block diagram of a code obfuscation device provided in this specification;
[0047] Figure 7 This is a block diagram of a business execution device provided in this specification. Detailed Implementation
[0048] The user information (including but not limited to user device information, user personal information, etc.) and data (including but not limited to data used for analysis, data stored, data displayed, etc.) involved in this manual are all information and data authorized by the user or fully authorized by all parties. Furthermore, the collection, use and processing of related data must comply with the relevant laws, regulations and standards of the relevant countries and regions, and corresponding operation portals are provided for users to choose to authorize or refuse.
[0049] Existing code obfuscation tools and techniques typically focus on single-language environments. Because different programming languages differ significantly in many aspects, such as runtime mechanisms and compilation, there is currently a lack of unified and efficient methods to systematically obfuscate the hybrid code resulting from cross-language integration. This leads to software products obtained through cross-language code integration being unable to be effectively protected.
[0050] To address this, this specification provides a code obfuscation method and a business execution method. First, the first language code and the second language code to be mixed are obtained. Then, the second language code is compiled and compressed into a compressed file. Following a preset encoding method, the compressed file is data-encoded, and the string corresponding to the encoded compressed file is written into the first language code as a constant, resulting in the mixed code. Finally, the mixed code is obfuscated using a preset security compiler to obtain the corresponding executable file. This method effectively yields an executable file resistant to reverse engineering attacks, thereby improving the overall execution security of the mixed code.
[0051] In this specification, first language code can refer to code that is compiled into machine code and executed directly by the operating system on which the first language code is based, and can refer to code written in programming languages such as C++ and C. Second language code can refer to code that is compiled into bytecode and executed by the virtual machine corresponding to the second language code, and can refer to code written in programming languages such as Java.
[0052] For ease of description, the following explanation will only use C++ code as the first language and Java code as the second language to illustrate one of the code obfuscation and business execution methods provided in this manual. The same applies to other cases, such as C code as the first language and Java code as the second language.
[0053] To clearly describe the code obfuscation and business execution method provided in this specification, some concepts appearing in this specification will be explained first.
[0054] Java Virtual Machine (JVM): An abstract computing machine that provides a runtime environment for Java programs and can execute instructions compiled into Java bytecode.
[0055] The JVM is actually more like a "translator." It can understand general-purpose bytecode and then tell the underlying hardware what to do by calling functions of the local operating system. Therefore, when Java source code is compiled into bytecode (.class files) using a Java compiler, this bytecode is not specific to any particular hardware machine. As long as the JVM is installed on the operating system of these machines, this bytecode can run.
[0056] The Java Native Interface (JNI) allows Java code running in the JVM to interact and call code written in other programming languages (such as C, C++, assembly, etc.). Therefore, in the code obfuscation methods provided in this manual, JNI acts as a "bridge" between C++ code and Java code. C++ code can transmit control instructions through JNI to achieve functions such as JVM creation and method invocation.
[0057] In addition, C++ code can also transmit or receive business data (business data is the data that the C++ / Java code actually processes) through JNI. The transmission of business data can be divided into the following scenarios:
[0058] 1. Data Transfer of Basic Data Types: For basic data types, data is usually transferred directly via JNI. Java's basic types such as int, boolean, double, and char have corresponding types in JNI such as jint, jboolean, jdouble, and jchar. Their binary formats are usually directly compatible, so they can be passed by value. For example, C++ code can transfer a Jint to a Java method as a parameter or receive a jboolean returned by a Java method via JNI.
[0059] 2. Data requiring type conversion: Because C++ and Java are two different programming languages, some data types cannot be transmitted directly and need to be converted first. For example, C++ can request JNI to convert Java's jstring (UTF-16 format) data and copy it into char* (UTF-8 format) data that C++ can understand.
[0060] 3. Data that cannot be directly transmitted: For array-type data, what is often transmitted via JNI is not the data itself, but a pointer to its storage location. For example, C++ requests a pointer to the array data from JNI, and then retrieves the array data through the storage location pointed to by that pointer.
[0061] In practical applications, the interaction and calling mechanisms, as well as the data processing mechanisms specified in JNI, are all contained in the JNI header file. Through this JNI header file, it can be determined how to call the methods of the Java class and how to process the received and transmitted data when executing C++ code.
[0062] Class loader: A tool used by the JVM to dynamically load the bytecode (.class file) of a class into memory and convert it into a java.lang.Class object.
[0063] In practical applications, class loaders employ a parent delegation model, a bottom-up checking and top-down loading mechanism. That is, when a class loader receives a class loading request, it doesn't attempt to load the class itself first. Instead, it delegates the request upwards to its parent class loader. Above the parent class loader are multiple other class loaders, so if the parent class loader doesn't find the required class, it will pass the request up the hierarchy. Therefore, each level of class loaders performs this operation until it reaches the top-level BootstrapClassLoader.
[0064] Only when the parent class loader of a class loader reports that it cannot complete the class loading request will the class loader attempt to load the class itself.
[0065] Jar files are a type of packaged file format based on ZIP files. They are compiled by a Java compiler and used to aggregate many Java class files, related metadata, and resources (such as text, images, etc.) into a single file.
[0066] Jar files typically contain the following types of data:
[0067] 1. Compiled Java class files (.class files): This is the core and most basic content of the Jar file. These files are compiled from Java source code (.java files) and contain bytecode that the JVM can execute.
[0068] 2. Resource files: These mainly include other non-code files required for the application to run, such as configuration files, images and icons, localization files, audio files, and XML files.
[0069] 3. Metadata file: A very important special file, usually located in the META-INF / directory. It is a plain text file that contains descriptive information about the Jar file itself and its contents (i.e., metadata).
[0070] Obfuscation compilation refers to the process of compiling source code into executable files or intermediate code using specialized obfuscation methods. This involves various transformations and modifications to the code, making the final code difficult for humans to read and understand, while simultaneously retaining its original functionality. The core idea is to make the obfuscated code difficult to comprehend. However, this obfuscation is not for encryption purposes, but rather to increase the cost of attacks by increasing the difficulty of understanding and analysis.
[0071] Common obfuscation methods include the following:
[0072] Name obfuscation: Rename meaningful names of classes, methods, variables, fields, etc., to meaningless short characters (e.g., rename calculateTotalRevenue to 'a').
[0073] Control flow obfuscation: altering the execution flow structure of code, for example, breaking down a simple if-else or while loop into logically confusing but equivalent branches and jumps connected by goto statements.
[0074] Code and data obfuscation: inserting useless code that will never be executed (such as junk code), or implementing simple instructions in a complicated way.
[0075] Debugging information removal: Removes all debugging symbols, file names, line numbers, and other information from the compilation output.
[0076] The `findclass` method primarily locates and loads the bytecode of a class based on its fully qualified name, and then defines it as a `class` object.
[0077] In practical applications, Java's built-in class loader already implements the `findclass` method, which loads .class files from a specified file path or JAR file. However, if you need to load classes from non-standard locations, you need to define a custom class loader. This is done by inheriting the `classLoader` class and overriding its `findclass` method.
[0078] A class is primarily used to define and describe the common attributes and behaviors of a group of things. Attributes, typically represented as variables or fields in code, describe the characteristics or state of an object. Behaviors, typically represented as methods or functions in code, describe the operations that an object can perform.
[0079] The following section will describe one method of code obfuscation provided in this manual.
[0080] This manual provides a method for obfuscating content, which can be roughly divided into several processes, such as... Figure 1 As shown.
[0081] Figure 1 This diagram illustrates the steps involved in the code obfuscation method provided in this manual.
[0082] S100: Obtain the first language code and the second language code to be mixed.
[0083] In the code obfuscation methods provided in this manual, the execution subject can be a server used by the application or client developer, a server cluster consisting of multiple servers on the developer's platform, or a client installed on the server or server cluster. Of course, the execution subject can also be the terminal device used by the developer (such as a desktop computer, laptop computer, or other electronic device), or a client installed on the terminal device. For ease of explanation, the following description uses the terminal device as the execution subject to illustrate the code obfuscation methods provided in this manual.
[0084] In practical applications, cross-code integration allows developers to leverage the strengths of multiple programming languages to build complex and high-performance applications, particularly common in system programming, high-performance computing, embedded development, or domain-specific applications. Therefore, developers can write the necessary code for the terminal devices they use, based on their specific needs. In the case of embedding Java code within C++ code, the C++ code can be used to execute the developer's target business logic and determine when and how to call the Java code. The embedded Java code, on the other hand, is primarily used by the C++ code to execute the target business logic and achieve the specified functionality required for that business logic.
[0085] For example, a developer might create a quantitative trading client. The main body of the client is written in C++, while the functional modules that provide trading strategies can be written in Java.
[0086] For example, a developer might create a game program based on C++, where the core graphics and physics / audio engines are written in C++ to ensure performance. However, the developer might also want community members or mod authors to create plugins within the game, such as those implementing custom quests or damage statistics. These plugins could then be written in Java code.
[0087] In practical applications, there are many other examples of cross-code integration where Java code is embedded in C++ code, which will not be listed here.
[0088] In this manual, developers can use various programming software running on the terminal device to input their written C++ and Java code into the input interface, so that the terminal device can obtain the C++ and Java code written by the developer.
[0089] S102: Compile the second language code and compress and package it to obtain a compressed file.
[0090] After obtaining the aforementioned C++ and Java code, the terminal device can use a pre-set Java compiler to compile and compress the Java code, resulting in a compressed JAR file.
[0091] As described above, a Jar file is actually a packaged file format based on the ZIP file format. Jar files compress various files and bytecode required to execute Java code, making it easier to embed them into C++ code in a compressed form.
[0092] S104: According to a preset encoding method, the compressed file is encoded, and the string corresponding to the compressed file obtained after data encoding is written into the first language code as a constant to obtain mixed code.
[0093] In practical applications, users are often ultimately distributed with executable files such as .exe and .dll, which are compiled by a compiler. For C++ code, the executable file (binary machine code) obtained after compilation has strong resistance to decompilation. This is because some key information is lost in the compiled executable file, making the decompiled code uninterpretable or prone to errors.
[0094] For example, C++ code usually contains clear function names that reflect the original intent of the function. However, in the executable file obtained after compiling C++ code, these functions are usually only represented as a memory address (such as 0x00411A30). Therefore, by decompiling, you can often only get a function name that is modified based on the memory address (such as sub_411A30). However, it is obvious that this function name can no longer reveal the original intent of the function.
[0095] For example, the decompilation process can be roughly divided into two steps: the first step is disassembly, that is, reading the executable file byte by byte through a disassembler to translate the machine code into the corresponding assembly code; the second step is decompilation, that is, trying to analyze these assembly codes through a decompiler. The second step is actually full of guessing and inference, which often leads to errors in the variable types in the executable file compiled by decompilation.
[0096] Specifically, during the execution of an executable file, the processor doesn't care about the specific data type (such as int, float, etc.). The processor only knows that there is a string of bytes at a certain memory address, and it needs to perform specified operations on these bytes (such as addition, comparison, shift, etc.) through assembly code. However, after seeing this assembly code, the decompiler needs to guess the data type read from memory through the assembly code, which leads to the decompiler guessing the wrong data type.
[0097] For example, advanced functions like for, while, and if / else are compiled into simple jump instructions (such as jmp, je, jne) at the lower level. Decompilers need to painstakingly reconstruct the high-level control flow from these jumps, a process that is very complex and error-prone.
[0098] There are many other situations where decompilers encounter decompilation errors due to the corresponding executable file of C++ code, which will not be listed here.
[0099] In contrast to C++, the bytecode obtained after compiling Java code does not offer good decompilation results. This is mainly because bytecode is fundamentally different from binary machine code. Bytecode is designed to be interpreted and executed by the JVM, and it retains a large amount of high-level semantic information. Therefore, by utilizing this retained high-level semantic information, it can be easily decompiled by a decompiler to retrieve the corresponding code.
[0100] For the reasons mentioned above, it is necessary to embed Java code as part of C++ code within C++ code, thereby enabling the Java code portion to also possess good decompilation capabilities. To this end, in this manual, the terminal device can encode the Jar file according to a preset encoding method, obtaining the string corresponding to the encoded Jar file. This string can then be written into the C++ code as a constant, resulting in mixed code.
[0101] There are several encoding methods mentioned above. For example, base64 encoding can be used to encode the data in the Jar file to obtain a string that can be embedded as a constant.
[0102] In addition to the base64 encoding method mentioned above, other encoding methods can also be used, such as base58, base32, base16, etc. The code obfuscation method provided in this manual does not limit the encoding method used here.
[0103] Encoding the Jar file using the above data encoding method provides better protection for the Java code. Furthermore, embedding the encoded string from the Jar file as a constant into the C++ code ensures that the executable file generated from the compiled C++ code also possesses excellent decompilation capabilities, further protecting the Java code.
[0104] S106: Obfuscate the mixed code using a preset security compiler to obtain the executable file corresponding to the mixed code.
[0105] After obtaining the mixed code, it needs to be obfuscated using a pre-defined security compiler to obtain the corresponding executable file. The specific obfuscation methods, as mentioned above, can include name obfuscation, control flow obfuscation, and code and data obfuscation.
[0106] As can be seen from the above method, by writing Java code as a constant into C++ code and using a secure compiler to perform overall obfuscation on the resulting hybrid code, the final executable file can effectively resist reverse engineering attacks, thereby effectively improving the overall security of the hybrid code.
[0107] It should be noted that developers can declare the embedding location of Java code within C++ code through the terminal device, so that the terminal device can embed the Java code into the C++ code according to that embedding location.
[0108] The above method can be understood as the way in which the developer actively declares the embedding location. In this specification, the terminal device can also determine the embedding location of Java code in C++ code on its own.
[0109] Specifically, while acquiring C++ and Java code, the terminal device can also obtain functional description text for the Java code. This text describes the specific functions that the Java code can provide in the target business logic. The terminal device can then perform semantic analysis on this functional description text to identify keywords that describe the functional characteristics. These identified keywords often reflect the core features of the specified function.
[0110] The terminal device can further determine the target function in the C++ code that calls the Java code to implement the specified function based on the keyword, and determine the embedding position of the Java code based on the target function. The terminal device can determine the target function based on the keyword in various ways. For example, if a pre-established correspondence between keywords and functions is used, the target function can be determined through this correspondence. Another example is that the terminal device can input the keyword and prompt statement into a preset intelligent model. This intelligent model, through analysis of the input data, can determine the probability values of each function that matches the function corresponding to the keyword, and output the function with the highest probability as the target function. Other methods will not be elaborated upon here.
[0111] In determining the embedding location based on the objective function, the terminal device can first determine the position of the objective function in the C++ code, then extract the context code at that position and perform semantic analysis to determine the embedding location corresponding to the Java code. Finally, the terminal device can embed the string corresponding to the data-encoded Jar file as a constant into the C++ code according to this embedding location, resulting in hybrid code.
[0112] Furthermore, the C++ and Java code acquired by the terminal device can be written by the same developer, or they can be written by different developers. For example, one developer might be responsible for writing the C++ code, and another for writing the Java code. When multiple developers are responsible for writing the C++ and Java code respectively, the final result needs to be aggregated into the same terminal device.
[0113] Whether the developer actively declares the embedding location of Java code in C++ code, or the terminal device automatically analyzes and determines the embedding location of Java code in C++ code, after receiving the aggregated C++ code or Java code, the terminal device can generate a code mixing task for C++ code and Java code. Then, through the above methods, it can determine the embedding location of Java code in C++ code, generate mixed code, and further generate the final executable file.
[0114] In this specification, the hybrid code may contain strings that include the source file corresponding to a custom class loader. That is, the terminal device can compile the source file of the custom class loader to obtain the corresponding string, and then write this string, along with the string corresponding to the aforementioned Jar file, as constants into the C++ code to obtain the hybrid code. This is primarily to better protect the source file from being leaked, hence its embedding as part of the hybrid code. Since the custom class loader is closely related to the execution phase of the executable file, its role will be described in the subsequent business execution methods.
[0115] After completing the above obfuscation process, the generated executable file can be further distributed to various users, thereby providing each user with the required target services through the executable file. The following describes one service execution method provided in this specification.
[0116] Figure 2 This document provides a schematic diagram illustrating the steps involved in the business execution method described herein, specifically including the following steps:
[0117] S200: Obtain the executable file.
[0118] In this specification, the execution subject of the business execution method can be the user's terminal device, such as a desktop computer or laptop, or a device such as a server or server cluster (when the execution subject is a server or server cluster, it can be understood that after the developer develops the executable file, they provide it to the business party to which the server or server cluster belongs, so that the business party can execute the required target business based on the executable file). For ease of explanation, the following description uses a server as an example to illustrate the business execution method provided in this specification. The server can receive the executable file released by the developer or the developer's publisher, and then execute the target task through the executable file in subsequent processes. The executable file is generated using the code obfuscation method described above.
[0119] S202: When executing the target service by running the executable file, the executable file is loaded into a preset memory, and the virtual machine corresponding to the second language code is started.
[0120] Because the mixed code contains strings corresponding to Jar files that exist as constants, the JVM needs to be started during the execution of the executable file, and the Java code part is executed through this JVM in subsequent processes.
[0121] Specifically, the aforementioned JNI can be used to start the JVM when executing the target business logic by running an executable file. Specifically, during the execution of C++ code, the JVM engine library can be initialized by calling the JNI_CreateJavaVM function provided by the Java Development Kit (JDK) to start the JVM.
[0122] In practical applications, strings that are considered constants are stored by the secure compiler in the read-only data segment (usually called .rodata) of the generated executable file. This data segment is specifically used to store constant data, which is read-only during program execution to prevent accidental modification.
[0123] When the above executable file is executed, the loader of the operating system in the server will load the executable file into the preset memory. Since the string corresponding to the Jar file is placed in the read-only data segment of the executable file, this string will be placed in the read-only area of the preset memory.
[0124] In traditional implementations, Java code is often stored as a separate file on a predefined file system or disk. Therefore, when executing C++ code, if a class in Java code needs to be called, the default class loader is used to read the bytecode of the required class from the predefined file system or disk.
[0125] However, when JAR files are stored on the default file system or disk, some attackers may exploit web application vulnerabilities or system vulnerabilities to gain access to the server, and then directly read and download bytecode from the file system or disk, resulting in the leakage of core information such as the developer's core algorithms, business logic, and proprietary technologies.
[0126] Storing the corresponding bytecode in pre-defined memory using the methods described above effectively protects the bytecode. Storing bytecode in pre-defined memory doesn't mean it's impossible to retrieve it; rather, it's because stealing bytecode from memory is far more difficult than stealing it directly from the default file system or disk. This is primarily because: in memory, data in a Jar file isn't stored contiguously in single blocks; decoded data and the bytecode corresponding to individual classes after decompression can be distributed across different locations in memory, further increasing the complexity of completely reconstructing the original Jar file data from memory; secondly, even if an attacker can monitor memory, they must capture a memory snapshot at the correct time (i.e., before the data in the Jar file has been decoded but before it has been garbage collected by the JVM), a very short window that significantly increases the difficulty of capture. This is why, in this specification, the strings corresponding to the Jar files are written as constants into the C++ code.
[0127] S204: Decode the string in the executable file using the decoding method corresponding to the encoding method to obtain the compressed file.
[0128] S206: Parse the compressed file and determine the storage location of the bytecode corresponding to the target class to be called in the preset memory.
[0129] S208: Using the virtual machine corresponding to the second language code, retrieve the bytecode corresponding to the target class from the storage location and execute it to obtain the execution result.
[0130] In this specification, the server first needs to decode the aforementioned strings in the executable file using the decoding method corresponding to the above encoding method to decode the Jar file. Then, it allocates corresponding storage locations in preset memory for the bytecode corresponding to at least some of the classes contained in the decoded Jar file.
[0131] This process can typically be implemented in two ways. The first is on-demand class loading. When executing each line of code, if a method of a particular class needs to be called, that class can be used as the target class. A custom class loader then locates the entry name of this target class in the Jar file. Next, based on this entry name, only the bytecode of the target class is decompressed and read from the Jar file and stored in a temporary byte array in pre-allocated memory (this temporary byte array is the storage location allocated for the bytecode of the target class in pre-allocated memory). The JVM then uses this temporary byte array to call and execute the bytecode of the target class.
[0132] The second method involves loading all classes at once. This means that a custom class loader parses the structure of the decoded JAR file, iterates through all class entries, and then, according to these entries, decompresses and reads the bytecode corresponding to each class from the JAR file, storing these bytecode entries in a pre-created data table in memory. The JVM then retrieves and executes the required bytecode for the target class from this data table as needed.
[0133] The first method saves as much storage space as possible by loading only the classes that are actually used. The second method, on the other hand, allows the JVM to quickly call the bytecode corresponding to the target class, resulting in higher real-time performance, since all classes have been loaded.
[0134] Since the default class loader often reads the required bytecode from the default file system or disk, after storing the bytecode in the default memory, a custom class loader is needed to retrieve the required bytecode from the default memory.
[0135] Specifically, in this specification, the server can load a custom class loader to parse the aforementioned Jar file, determine the storage location of the target class's bytecode in a preset memory location, and then, through the JVM, retrieve the bytecode corresponding to the target class from that storage location and execute it to obtain the execution result. The server can load the custom class loader based on the string of the source file corresponding to the custom class loader carried in the executable file. Of course, if the executable file does not contain the string of the source file corresponding to the custom class loader, the server can load the custom class loader using the source file of the custom class loader obtained from the developer, or using the source file of a custom class loader written based on information provided by the developer.
[0136] In this process, the findclass method in the custom class loader is used to determine the mapping relationship between the target class and the storage location of the target class's corresponding bytecode. Then, based on this mapping relationship, the storage location of the target class's corresponding bytecode in the preset memory is determined.
[0137] As described in the above concept introduction, the findclass method can be understood as redefining the mapping relationship between class name and bytecode storage location. Therefore, by overriding the findclass method, the custom class loader can determine the storage location of the target class's bytecode directly in the preset memory based on the redefined mapping relationship instead of querying the target class's bytecode from the default file system or disk.
[0138] Furthermore, in practical applications, the JVM's class loading mechanism requests and queries classes based on fully qualified class names, while classes in Jar files are stored as file paths. Therefore, the custom loader described in this specification also needs to map fully qualified class names to the entry names of various classes in the Jar file, thereby ultimately retrieving the required bytecode.
[0139] In this process, the server first needs to convert the entry name of each class contained in the Jar file into its fully qualified class name using a custom class loader. Then, by overriding the `findclass` method, a mapping relationship is established between the fully qualified class name and the storage location of the bytecode corresponding to the target class. This way, when executing C++ code needs to call a method of the target class in Java code, the JVM can locate the storage location of the target class's bytecode based on the fully qualified class name and this mapping relationship. This allows the JVM to retrieve and execute the corresponding bytecode from that storage location.
[0140] Furthermore, after the server obtains the storage location of the bytecode corresponding to the target class through the custom class loader, it does not directly read the bytecode corresponding to the target class from the storage location and transmit it to the JVM. Instead, it transmits the storage location (usually in the form of a memory pointer) to the JVM, and the JVM reads the bytecode corresponding to the target class from the preset memory and executes it based on the storage location.
[0141] During this process, the JVM does not immediately execute the bytecode corresponding to the target class after reading it. Instead, it copies the bytecode of the target class from its storage location and allocates a storage space (such as the method area / metaspace) for it within the JVM-managed memory space, thus caching the bytecode in that space. Next, a security verification is performed on the bytecode to determine its safety. Only after the security verification is passed can the bytecode of the target class be executed.
[0142] After obtaining the execution result of the bytecode through the JVM, the result needs to be passed back to the C++ code so that the C++ code can continue executing subsequent code. In this process, since Java and C++ are different programming languages, the server needs to use JNI to convert the execution result obtained by the JVM into the data format or data type required by C++ before passing it back to the C++ code for execution. This process has been explained in detail with examples in the JNI section above and will not be repeated here.
[0143] As can be seen from the methods described above, not only can Java code be encoded as a string and embedded into C++ code, but the source file of a custom class loader can also be encoded as a string and embedded into C++ code. This way, when the executable files are executed, data loading is completed in memory, without retrieving bytecode or the source file of the custom class loader from the file system or disk, thus further protecting core data from leakage. Moreover, by using obfuscated compilation, resistance to reverse engineering attacks can be effectively achieved, thereby significantly improving the overall security of the hybrid code.
[0144] To further illustrate this, the following will explain the entire process in conjunction with the first stage of generating the executable file through obfuscation and the second stage of executing the executable file. Figure 3 and Figure 4 As shown.
[0145] Figure 3 This diagram illustrates an example of generating an executable file through obfuscation, as provided in this specification.
[0146] Figure 3 The main process shown in the text is the effective protection of Java code. In this process, the Java code is compiled by the Java compiler to obtain a Jar file. Then, the Jar file is encoded using the base64 encoding method mentioned above to obtain a text string.
[0147] The server can embed this string as a constant into the C++ code, making it part of the overall C++ code and resulting in mixed code. Finally, a pre-defined secure compiler performs secure obfuscation compilation on the mixed code. During this process, the string is placed into the .rodata read-only data segment of the compiled file. After secure obfuscation compilation is complete, the executable files (.exe / .dll) that need to be distributed are obtained.
[0148] Figure 4 This diagram illustrates the execution of an executable file generated using an obfuscation method, as provided in this specification.
[0149] Since C++ is the main body of the program in the aforementioned executable file, starting and executing this executable file is actually starting the execution of the main C++ program. During this process, the C++ code calls the JNI function `JNI_CreateJavaVM()` to initialize the JVM. Afterward, the C++ code uses JNI to locate a pre-prepared Java utility class (such as `JarLoader`) and calls its static method, passing a base64-encoded string as a parameter to that method.
[0150] Next, the invoked static method begins execution. It receives the string from C++ and decodes it using the Java standard library to reconstruct the original JAR file. Then, a custom class loader is instantiated, and the decoded JAR file is passed to its constructor to load the required target class. Internally, the custom class loader searches for, extracts, and decompresses the bytecode of the target class from the pre-defined JAR file in memory, then calls the JVM's `defineClass` method to formally load it into the JVM. After the target class is successfully loaded, its corresponding methods are invoked, embedded in the Java code, and execution begins.
[0151] After the call to the corresponding method of the target class is completed, control is returned to the C++ code. That is, the data obtained from calling the corresponding method of the target class is returned to the C++ code, which then continues execution according to the received data. After the execution of the C++ program is complete, the JVM can be destroyed, and the resources required to create the JVM can be cleared and released.
[0152] It should be noted that, as mentioned at the beginning, the code obfuscation and business execution methods provided in this manual are mainly illustrated by embedding Java code in C++ code. In practical applications, the first language code and the second language code can also be in other forms. Therefore, the methods provided in this manual are also applicable to the mixing of code written in other different programming languages.
[0153] For example, Lua code can be embedded in C++ code. The process involves first compiling the Lua code into bytecode and compressing it, then encoding it using a preset encoding to obtain a corresponding string. This string is then written as a constant into the C++ code, resulting in hybrid code. When executing the executable file corresponding to this hybrid code, the strings in the executable file are first decoded using the decoding method corresponding to the encoding method, resulting in a compressed file. This compressed file is then parsed, and the storage location of the bytecode to be loaded is determined from memory using Lua-specific functions (such as `lua_load`) and a custom reader. The bytecode is then loaded, and finally, the Lua virtual machine executes the loaded bytecode. Other combinations are not illustrated here.
[0154] Figure 5 This is a schematic structural diagram of a device provided in this manual. Please refer to it. Figure 5At the hardware level, the device includes a processor 502, an internal bus 504, a network interface 506, memory 508, and non-volatile memory 510, and may also include other hardware required for its functions. One or more embodiments of this specification can be implemented in software, for example, the processor 502 reads the corresponding computer program from the non-volatile memory 510 into memory 508 and then runs it. Of course, in addition to software implementation, one or more embodiments of this specification do not exclude other implementation methods, such as logic devices or a combination of hardware and software, etc. That is to say, the execution subject of the following processing flow is not limited to each logic unit, but can also be hardware or logic devices.
[0155] Please refer to Figure 6 The code obfuscation device provided in this specification can be applied to, for example... Figure 5 The device shown, in order to implement the technical solution of this specification, includes:
[0156] The acquisition module 600 is used to acquire the first language code and the second language code to be mixed. The first language code is used to provide the target service. When the target service is executed, the second language code is called by the first language code to realize the specified function required when executing the target service. The first language code is compiled into machine code and executed by the operating system on which the first language code is based. The second language code is compiled into bytecode and executed by the virtual machine corresponding to the second language code.
[0157] The compilation module 602 is used to compile the second language code and compress and package it to obtain a compressed file, wherein the compressed file contains the bytecode corresponding to the second language code;
[0158] The encoding module 604 is used to encode the compressed file according to a preset encoding method, and write the string corresponding to the compressed file after data encoding as a constant in the first language code into the first language code to obtain mixed code;
[0159] The obfuscation module 606 is used to obfuscate the mixed code using a preset security compiler to obtain the executable file corresponding to the mixed code.
[0160] Optionally, the first language code includes C++ code or C code, the second language code includes Java code, and when the second language code is Java code, the virtual machine is a Java Virtual Machine, and the compressed file is a Jar file.
[0161] Optionally, the encoding module 604 is specifically used to: compile the source file of the custom class loader to obtain the string corresponding to the custom class loader; the custom class loader is used to determine the storage location of the bytecode corresponding to each class contained in the Jar file when executing the executable file, so that the Java Virtual Machine obtains and executes the bytecode based on the storage location; and write the string corresponding to the custom class loader and the string corresponding to the Jar file as constants in the first language code into the first language code to obtain mixed code.
[0162] Please refer to Figure 7 The business execution device provided in this specification can be applied to, for example... Figure 5 The device shown, in order to implement the technical solution of this specification, includes:
[0163] The acquisition module 700 is used to acquire an executable file. The executable file is obtained by obfuscating the mixed code through a preset security compiler. The mixed code is obtained by writing a string obtained by data encoding a compressed file according to a preset encoding method as a constant into the first language code. The compressed file is obtained by compiling the second language code and compressing and packaging it. The first language code is used to provide the target business. The second language code is called by the first language code to realize the specified function required when executing the target business. The first language code is compiled into machine code and executed by the operating system on which the first language code is based. The second language code is compiled into bytecode and executed by the virtual machine corresponding to the second language code.
[0164] The startup module 702 is used to load the executable file into a preset memory and start the virtual machine corresponding to the second language code when the target business is executed by running the executable file;
[0165] The decoding module 704 is used to decode the string in the executable file using the decoding method corresponding to the encoding method to obtain the compressed file;
[0166] Loading module 706 is used to parse the compressed file and determine the storage location of the bytecode corresponding to the target class to be called in the preset memory;
[0167] The execution module 708 is used to obtain the bytecode corresponding to the target class from the storage location through the virtual machine corresponding to the second language code and execute it to obtain the execution result.
[0168] Optionally, the first language code includes C++ code or C code, the second language code includes Java code, and when the second language code is Java code, the virtual machine is a Java Virtual Machine, and the compressed file is a Jar file;
[0169] The loading module 706 is specifically used to load a custom class loader to parse the Jar file through the custom class loader, and to determine the mapping relationship between the target class and the storage location of the corresponding bytecode of the target class through the findclass method in the custom class loader, wherein the mapping relationship is defined by overriding the findclass method; and to determine the storage location of the bytecode corresponding to the target class in the preset memory according to the mapping relationship.
[0170] Optionally, the loading module 706 is specifically configured to: parse the Jar file using the custom class loader to traverse the entry names of all classes contained in the Jar file; decompress and read the bytecode corresponding to each class from the Jar file according to the entry names, and store the bytecode corresponding to each class in a data table created in a preset memory; and rewrite the findclass method according to the storage location of the bytecode corresponding to each class in the data table to establish a mapping relationship between each class and the storage location of the bytecode corresponding to each class.
[0171] Optionally, the loading module 706 is specifically used to convert the entry name of each class into a fully qualified class name; and to rewrite the findclass method according to the storage location of the bytecode corresponding to each class in the data table, so as to establish a mapping relationship between the fully qualified class name of each class and the storage location of the bytecode corresponding to each class.
[0172] The loading module 706 is specifically used to determine the storage location of the bytecode corresponding to the target class in the preset memory based on the fully qualified class name of the target class and the mapping relationship.
[0173] Based on the same concept as the methods described above, this specification also provides an electronic device, including: a processor; a memory for storing processor-executable instructions; wherein the processor performs the steps of the method as described in any of the above embodiments by executing the executable instructions.
[0174] Based on the same concept as the methods described above, this specification also provides a computer-readable storage medium having computer instructions stored thereon that, when executed by a processor, implement the steps of the methods as described in any of the above embodiments.
[0175] Based on the same concept as the methods described above, this specification also provides a computer program product, including a computer program / instructions that, when executed by a processor, implement the steps of the methods as described in any of the above embodiments.
[0176] This specification can be described in the general context of computer-executable instructions that are executed by a computer, such as program modules. Generally, program modules include routines, programs, objects, components, data structures, etc., that perform a specific task or implement a specific abstract data type. This specification can also be practiced in distributed computing environments, where tasks are performed by remote processing devices connected via a communication network. In distributed computing environments, program modules can reside in local and remote computer storage media, including storage devices.
[0177] The various embodiments in this specification are described in a progressive manner. Similar or identical parts between embodiments can be referred to interchangeably. Each embodiment focuses on describing the differences from other embodiments. In particular, the system embodiments are basically similar to the method embodiments, so the description is relatively simple; relevant parts can be referred to the descriptions in the method embodiments.
[0178] The above are merely embodiments of this specification and are not intended to limit this specification. Various modifications and variations can be made to this specification by those skilled in the art. Any modifications, equivalent substitutions, improvements, etc., made within the spirit and principles of this specification should be included within the scope of the claims of this specification.
Claims
1. A code obfuscation method, comprising: Obtain the first language code and the second language code to be mixed. The first language code is used to provide the target service. When the target service is executed, the second language code is called by the first language code to realize the specified function required when executing the target service. The first language code is compiled into machine code and executed by the operating system on which the first language code is based. The second language code is compiled into bytecode and executed by the virtual machine corresponding to the second language code. The second language code is compiled and compressed to obtain a compressed file, which contains the bytecode corresponding to the second language code; According to a preset encoding method, the compressed file is encoded, and the string corresponding to the compressed file obtained after data encoding is written into the first language code as a constant to obtain mixed code; The mixed code is obfuscated using a preset security compiler to obtain the executable file corresponding to the mixed code.
2. The method as described in claim 1, wherein the first language code includes C++ code or C code, the second language code includes Java code, and when the second language code is Java code, the virtual machine is a Java Virtual Machine, and the compressed file is a Jar file.
3. The method as described in claim 2, wherein the string corresponding to the compressed file obtained after data encoding is written as a constant in the first language code and then written into the first language code to obtain hybrid code, specifically includes: Compile the source file of the custom class loader to obtain the string corresponding to the custom class loader. The custom class loader is used to determine the storage location of the bytecode corresponding to each class contained in the Jar file when the executable file is executed, so that the Java Virtual Machine can obtain and execute the bytecode based on the storage location. The strings corresponding to the custom class loader and the Jar file are written into the first language code as constants to obtain hybrid code.
4. A business execution method, comprising: An executable file is obtained by obfuscating the hybrid code using a preset security compiler. The hybrid code is obtained by writing a string obtained by encoding the compressed file according to a preset encoding method as a constant into the first language code. The compressed file is obtained by compiling the second language code and compressing and packaging it. The first language code is used to provide the target business. The second language code is called by the first language code to implement the specified function required when executing the target business. The first language code is compiled into machine code and executed by the operating system on which the first language code is based. The second language code is compiled into bytecode and executed by the virtual machine corresponding to the second language code. When executing the target service by running the executable file, the executable file is loaded into a preset memory, and the virtual machine corresponding to the second language code is started; The string in the executable file is decoded using the decoding method corresponding to the encoding method to obtain the compressed file; The compressed file is parsed, and the storage location of the bytecode corresponding to the target class to be called is determined in the preset memory. The virtual machine corresponding to the second language code retrieves the bytecode corresponding to the target class from the storage location and executes it to obtain the execution result.
5. The method as described in claim 4, wherein the first language code includes C++ code or C code, the second language code includes Java code, and when the second language code is Java code, the virtual machine is a Java Virtual Machine, and the compressed file is a Jar file; Parsing the compressed file and determining the storage location of the bytecode corresponding to the target class to be called in the preset memory includes: A custom class loader is loaded to parse the Jar file and to determine the mapping relationship between the target class and the storage location of the corresponding bytecode of the target class through the findclass method in the custom class loader. The mapping relationship is defined by overriding the findclass method. Based on the mapping relationship, the storage location of the bytecode corresponding to the target class in the preset memory is determined.
6. The method as described in claim 5, wherein rewriting the findclass method to define the mapping relationship specifically includes: The custom class loader parses the Jar file to traverse the entry names of all classes contained in the Jar file; Based on the entry name, the bytecode corresponding to each class is decompressed and read from the Jar file in sequence, and the bytecode corresponding to each class is stored in a data table created in a preset memory. The findclass method is rewritten according to the storage location of the bytecode corresponding to each class in the data table, so as to establish a mapping relationship between each class and the storage location of the bytecode corresponding to each class.
7. The method as described in claim 6, wherein the findclass method is rewritten according to the storage location of the bytecode corresponding to each class in the data table to establish a mapping relationship between each class and its corresponding bytecode, specifically including: Convert the entry name of each class into a fully qualified class name; According to the storage location of the bytecode corresponding to each class in the data table, the findclass method is rewritten to establish a mapping relationship between the fully qualified class name of each class and the storage location of the bytecode corresponding to each class; Based on the mapping relationship, determining the storage location of the bytecode corresponding to the target class in the preset memory specifically includes: Based on the fully qualified class name of the target class and the mapping relationship, determine the storage location of the bytecode corresponding to the target class in the preset memory.
8. An electronic device, comprising: processor; A memory for storing processor-executable instructions; wherein the processor implements the steps of the method as described in any one of claims 1 to 7 by executing the executable instructions.
9. A computer-readable storage medium having stored thereon computer instructions that, when executed by a processor, implement the steps of the method as claimed in any one of claims 1 to 7.
10. A computer program product comprising a computer program / instructions that, when executed by a processor, implement the steps of the method as described in any one of claims 1 to 7.