Binary Xor¶
Level: 201 · for anyone with a hex editor open
One line: X[i] ^= Operand flips every bit the operand has, so doing it twice gives the file back — which makes single-byte Xor the oldest way to disguise bytes and no way at all to hide them, since one known letter of the original gives away the key.
What the dialog does¶
The manual writes Binary Xor as X[i] ^= Operand (Hex Operations ↗). Each bit of the result is 1 where exactly one of the value and the operand has a 1: the operand's 1s flip bits and its 0s leave them. A flip loses nothing — it is its own inverse — and that single property is behind everything on this page.
In Python¶
Verified output of binary_xor_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. XOR 20 SWAPS THE CASE OF ASCII LETTERS
------------------------------------------------------------------------
Bit 5 is set in a lowercase ASCII letter and clear in a capital, so
flipping it swaps them:
'Cafe' 43 61 66 65 -> 63 41 46 45 'cAFE'
'Café' 43 61 66 c3 a9 -> 63 41 46 e3 89 not UTF-8
On é the flip also lands on the lead byte: C3 becomes E3, which
starts a three-byte character, and only one byte follows it.
2. XOR TWICE GIVES THE FILE BACK
------------------------------------------------------------------------
every byte, every operand, 65,536 pairs True
café through Xor 20 twice 'café'
Even the invalid UTF-8 in the middle comes back, because no bit was
ever lost -- only flipped.
3. A ONE-BYTE KEY IS NOT A SECRET
------------------------------------------------------------------------
café, Xor 0x5A: 39 3b 3c 99 f3
hidden[0] ^ ord('c') 0x5A the key, from one known letter
hidden[i] ^ hidden[j] == café[i] ^ café[j] True the key cancels out
Anyone who can guess one byte of the original has the key, and
anyone who has two hidden bytes has their Xor without it.
4. OPERAND STEP GIVES EVERY BYTE ITS OWN KEY
------------------------------------------------------------------------
b'aaaa', Xor 0x5A:
Operand Step 0 3b 3b 3b 3b four equal bytes for four equal letters
Operand Step 1 3b 3a 3d 3c no two alike
Step 1 again, same key b'aaaa'
The key now changes with the position, so repeated letters stop
showing. It is still a key anyone can recover the same way.
5. A WIDER KEY LANDS WHERE THE ENDIAN TOGGLE PUTS IT
------------------------------------------------------------------------
Xor 0x1234 as Unsigned Short over four zero bytes writes the key
itself, in the order the toggle names:
little 34 12 34 12
big 12 34 12 34
Xor 20 swaps case, and flips a lead byte too¶
Bit 5 is set in lowercase ASCII letters and clear in capitals, so Xor 20 swaps them: section 1 turns Cafe into cAFE. On Café the same flip also reaches the é, whose lead byte C3 becomes E3 — the start of a three-byte character — and the result is not UTF-8. Unlike Binary Or, though, nothing was destroyed: section 2 runs Xor 20 a second time and gets café back, invalid middle and all, and checks the rule for every byte against every operand.
A one-byte key is not a secret¶
Section 3 hides café behind the key 0x5A and then takes the disguise apart two ways. Anyone who knows or guesses one letter of the original has the key: the first hidden byte Xor c is 5A. And anyone holding two hidden bytes has the Xor of the two original bytes without the key at all, because the key flips the same bits in both and cancels. This is why a single-byte Xor turns up in malware and in file formats that want to stop casual reading, and never in anything that needs to keep a secret. Rotation is not encryption makes the same argument about ROT13.
Section 4 adds Operand Step 1, so each byte gets its own key and four equal letters no longer give four equal bytes. It hides the repetition, not the key: the key at every position is still one known letter away, and the same settings run again restore the file.
A wider key lands where the toggle puts it¶
Section 5 applies Xor 0x1234 as an Unsigned Short to zero bytes, which writes the key itself into the file: 34 12 34 12 under Little Endian and 12 34 12 34 under Big. The same key typed into the box is two different byte patterns, and a file disguised under one setting comes back only under the same one.
What the manual does not say¶
- Whether Operand Step wraps when it carries the key past the top of the type.
- Whether Binary Xor is offered for Float and Double, where C has no
^.
If you are coming from Python or ABAP¶
Python. bytes(b ^ k for b in data) is Xor with a one-byte key, and bytes(b ^ key[i % len(key)] for i, b in enumerate(data)) with a repeating multi-byte one. Both are their own inverse, as section 2 shows, so the same function hides and reveals. For whole buffers of equal length, (int.from_bytes(a, 'big') ^ int.from_bytes(b, 'big')).to_bytes(len(a), 'big') is one expression.
ABAP. (Not machine-checked — CI cannot run ABAP.) BIT-XOR is one of the four bit operators ↗, and like the others it works on byte-like operands — the documentation's own example gives 0110 for '0011' BIT-XOR '0101'. Applied twice with the same operand it restores the original, exactly as section 2 does, and for the same reason.
Try it¶
- Xor
5Aover a copy of a text file, look at the result, then Xor5Aagain and confirm the file is back. - Ask someone to Xor a copy of a JSON or HTML file with a byte they do not tell you. Recover it from the first byte of the result, which you know is
{or<. - Xor
0x1234as Unsigned Short over sixteen zero bytes under each Endian setting, and read the pattern in the hex column. - Run Xor with Operand Step 1 over a run of equal bytes, then run it again with the same settings and confirm the file is back.
See also¶
- Binary Invert — Xor
FF, with no operand - Binary Or — the mask that cannot be undone
- Rotation is not encryption — ROT13, and why a reversible rule is not a secret
- Which settings can change the result — Xor's row, and why the sign never matters to it