如何读取Java Class文件并替换其中的静态字符串内容?
Alright, let's break this down step by step. You want to swap the static string "Hello" with "Hi" in your compiled test.class file—here's how to do it properly, including a ready-to-use code template.
Class files are binary formats, so we'll read the entire file into a byte array using standard Java I/O. This gives us full access to modify the underlying bytes directly, no fancy libraries needed.
Static strings in a class file live in the constant pool—a dedicated section that stores all literal values, class names, method names, and other reusable data. Specifically, your "Hello" is stored as a CONSTANT_Utf8_info entry, which follows this structure:
- 1-byte tag (fixed value = 1, marking it as a UTF-8 string)
- 2-byte unsigned length (stored in big-endian order)
- N-byte UTF-8 encoded string (where N matches the length value)
To replace "Hello" with "Hi", you need to:
- Locate the
CONSTANT_Utf8_infoentry that holds "Hello" - Update the 2-byte length from 5 (length of "Hello") to 2 (length of "Hi")
- Replace the 5 UTF-8 bytes of "Hello" with the 2 bytes of "Hi"
Important: If you skip updating the length value, the class file will be corrupted and won't run—don't forget this step!
Here's a self-contained Java program that handles reading, modifying, and writing back the class file. It's tailored to your specific use case but can be adapted for similar tasks:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ClassStringReplacer { public static void main(String[] args) throws IOException { // Configure your paths and target strings here String classFilePath = "test.class"; String targetString = "Hello"; String replacementString = "Hi"; // Step 1: Read the entire class file into a byte array byte[] classBytes = readClassFile(classFilePath); // Step 2: Modify the constant pool to swap the strings modifyConstantPool(classBytes, targetString, replacementString); // Step 3: Write the modified bytes back to the class file writeClassFile(classFilePath, classBytes); System.out.println("Success! Replaced \"" + targetString + "\" with \"" + replacementString + "\""); } private static byte[] readClassFile(String path) throws IOException { try (FileInputStream fis = new FileInputStream(path)) { byte[] buffer = new byte[fis.available()]; fis.read(buffer); return buffer; } } private static void modifyConstantPool(byte[] classBytes, String target, String replacement) { // Constant pool starts at offset 8 (after 4-byte magic number + 4-byte version info) int constantPoolCount = ((classBytes[8] & 0xFF) << 8) | (classBytes[9] & 0xFF); int offset = 10; // Start of the first constant pool entry for (int i = 1; i < constantPoolCount; i++) { byte tag = classBytes[offset]; if (tag == 1) { // We found a CONSTANT_Utf8_info entry // Read the string length (big-endian 2 bytes) int length = ((classBytes[offset + 1] & 0xFF) << 8) | (classBytes[offset + 2] & 0xFF); // Extract the actual string from the byte array String str = new String(classBytes, offset + 3, length); if (str.equals(target)) { // Update the length to match the replacement string byte[] replacementLength = intTo2Bytes(replacement.length()); classBytes[offset + 1] = replacementLength[0]; classBytes[offset + 2] = replacementLength[1]; // Replace the string bytes byte[] replacementBytes = replacement.getBytes(); System.arraycopy(replacementBytes, 0, classBytes, offset + 3, replacementBytes.length); // Exit early—we found and modified our target string break; } // Move offset to the next constant pool entry offset += 3 + length; } else { // Skip over other constant pool entry types (fixed sizes) switch (tag) { case 7: case 8: case 16: offset += 3; break; case 9: case 10: case 11: case 12: case 18: offset += 5; break; case 3: case 4: offset += 5; break; case 5: case 6: offset += 9; i++; // Long/Double take two slots in the count break; default: offset++; break; } } } } private static byte[] intTo2Bytes(int value) { // Convert an int to a 2-byte big-endian array return new byte[]{(byte) (value >> 8), (byte) value}; } private static void writeClassFile(String path, byte[] bytes) throws IOException { try (FileOutputStream fos = new FileOutputStream(path)) { fos.write(bytes); } } }
- Compile your original
test.javato generatetest.class - Compile this
ClassStringReplacer.javaprogram - Run
ClassStringReplacer—it will modify yourtest.classfile in place - Run
java test—you'll see the output is nowHiinstead ofHello
A quick heads-up: This works perfectly for your simple test case. For more complex class files (like obfuscated code, or where the same string appears multiple times), you might need to add extra checks to target the exact constant pool entry used in your println call. But for your given code, this will do the trick.
内容的提问来源于stack exchange,提问作者Srinivas Jayaram

