Class: Omnizip::Filters::BcjX86
- Inherits:
-
FilterBase
- Object
- FilterBase
- Omnizip::Filters::BcjX86
- Defined in:
- lib/omnizip/filters/bcj_x86.rb
Overview
BCJ (Branch/Call/Jump) filter for x86/x64 executables.
This filter preprocesses x86 machine code by converting relative addresses in CALL (0xE8) and JMP (0xE9) instructions to absolute addresses. This transformation makes the code more compressible because the addresses become position-independent.
The filter is reversible and works on 5-byte boundaries (1-byte opcode + 4-byte address).
Constant Summary collapse
- OPCODE_CALL =
x86 CALL opcode
0xE8- OPCODE_JMP =
x86 JMP opcode
0xE9- ADDRESS_SIZE =
Size of x86 address (4 bytes, little-endian)
4- INSTRUCTION_SIZE =
Instruction size (opcode + address)
5
Class Method Summary collapse
-
.metadata ⇒ Hash
Get metadata about this filter.
Instance Method Summary collapse
-
#decode(data, position = 0) ⇒ String
Decode (postprocess) x86 executable data after decompression.
-
#encode(data, position = 0) ⇒ String
Encode (preprocess) x86 executable data for compression.
Class Method Details
.metadata ⇒ Hash
Get metadata about this filter.
131 132 133 134 135 136 137 138 |
# File 'lib/omnizip/filters/bcj_x86.rb', line 131 def { name: "BCJ-x86", description: "Branch/Call/Jump converter for x86/x64 " \ "executables", architecture: "x86/x64", } end |
Instance Method Details
#decode(data, position = 0) ⇒ String
Decode (postprocess) x86 executable data after decompression.
Reverses the encoding by converting absolute addresses back to relative addresses.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/omnizip/filters/bcj_x86.rb', line 93 def decode(data, position = 0) return data.dup if data.bytesize < INSTRUCTION_SIZE result = data.b i = 0 limit = data.bytesize - INSTRUCTION_SIZE while i <= limit opcode = result.getbyte(i) # Check for CALL or JMP instruction if [OPCODE_CALL, OPCODE_JMP].include?(opcode) # Extract absolute address (4 bytes, little-endian) absolute = extract_int32_le(result, i + 1) # Calculate relative offset # Offset should be relative to position AFTER instruction offset = absolute - (position + i + INSTRUCTION_SIZE) # Check if result is a valid relative address if valid_relative_address?(offset) write_int32_le(result, i + 1, offset) end # Skip past this instruction i += INSTRUCTION_SIZE else i += 1 end end result end |
#encode(data, position = 0) ⇒ String
Encode (preprocess) x86 executable data for compression.
Scans for E8/E9 opcodes and converts relative addresses to absolute addresses.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/omnizip/filters/bcj_x86.rb', line 51 def encode(data, position = 0) return data.dup if data.bytesize < INSTRUCTION_SIZE result = data.b i = 0 limit = data.bytesize - INSTRUCTION_SIZE while i <= limit opcode = result.getbyte(i) # Check for CALL or JMP instruction if [OPCODE_CALL, OPCODE_JMP].include?(opcode) # Extract relative offset (4 bytes, little-endian) offset = extract_int32_le(result, i + 1) # Check if this is a valid relative address # Valid addresses have high byte of 0x00 or 0xFF if valid_relative_address?(offset) # Convert relative to absolute # Address is relative to position AFTER instruction absolute = offset + position + i + INSTRUCTION_SIZE write_int32_le(result, i + 1, absolute) end # Skip past this instruction i += INSTRUCTION_SIZE else i += 1 end end result end |