Decrypt Ruckus Unleashed / ZoneDirector Backups
This code works for ZoneDirector & Unleashed backups and debug logs, and also ZoneDirector Software Images.
TIP
Use e.g. 7-Zip to explore/extract the decrypted .tgz
archive.
Online Tool
Your internet browser will use Javascript to do the decryption.
No data from the encrypted file will leave your PC.
Ruckus Crypt bash Functions (using Python)
WARNING
Python is very slow.
To keep things speedy(ish), this code is quite ugly and complicated, and uses lots of memory.
The online tool, above, and Powershell functions, below, are much faster.
bash
function rks_encrypt {
RUCKUS_SRC="$1" RUCKUS_DEST="$2" python3 - <<END
import os
import struct
input_path = os.environ['RUCKUS_SRC']
output_path = os.environ['RUCKUS_DEST']
(xor_int, xor_flip) = struct.unpack('QQ', b')\x1aB\x05\xbd,\xd6\xf25\xad\xb8\xe0?T\xc58')
structInt8 = struct.Struct('Q')
with open(input_path, "rb") as input_file:
with open(output_path, "wb") as output_file:
input_len = os.path.getsize(input_path)
input_blocks = input_len // 8
output_int = 0
input_data = input_file.read(input_blocks * 8)
for input_int in struct.unpack_from(str(input_blocks) + "Q", input_data):
output_int ^= xor_int ^ input_int
xor_int ^= xor_flip
output_file.write(structInt8.pack(output_int))
input_block = input_file.read()
input_padding = 8 - len(input_block)
input_int = structInt8.unpack(input_block.ljust(8, bytes([input_padding | input_padding << 4])))[0]
output_int ^= xor_int ^ input_int
output_file.write(structInt8.pack(output_int))
END
}
bash
function rks_decrypt {
RUCKUS_SRC="$1" RUCKUS_DEST="$2" python3 - <<END
import os
import struct
input_path = os.environ['RUCKUS_SRC']
output_path = os.environ['RUCKUS_DEST']
(xor_int, xor_flip) = struct.unpack('QQ', b')\x1aB\x05\xbd,\xd6\xf25\xad\xb8\xe0?T\xc58')
structInt8 = struct.Struct('Q')
with open(input_path, "rb") as input_file:
with open(output_path, "wb") as output_file:
input_data = input_file.read()
previous_input_int = 0
for input_int in struct.unpack_from(str(len(input_data) // 8) + "Q", input_data):
output_bytes = structInt8.pack(previous_input_int ^ xor_int ^ input_int)
xor_int ^= xor_flip
previous_input_int = input_int
output_file.write(output_bytes)
output_padding = int.from_bytes(output_bytes[-1:], 'big') & 0xf
output_file.seek(-output_padding, os.SEEK_END)
output_file.truncate()
END
}
Decrypt a backup
bash
rks_decrypt ruckus_db_073122_14_17.bak ruckus_db_073122_14_17.bak.tgz
Re-encrypt a backup
bash
rks_encrypt ruckus_db_073122_14_17.bak.tgz ruckus_db_073122_14_17.modded.bak
Ruckus Crypt PowerShell Functions (using C#)
WARNING
If you want to process your backup for re-upload then you need to be careful to use unix line endings in a few places, so it will probably be easier to tinker in WSL if you're a Windows user.
powershell
Add-Type -Language CSharp @"
using System;
using System.IO;
namespace Ms264556
{
public static class Ruckus
{
private static readonly byte[] XorBytes = new byte[] { 0x29, 0x1A, 0x42, 0x05, 0xbd, 0x2c, 0xd6, 0xf2, 0x1c, 0xb7, 0xfa, 0xe5, 0x82, 0x78, 0x13, 0xca };
public static void DecryptFile(string sourcePath, string destinationPath)
{
var inputBlock = new byte[8];
var previousInputBlock = new byte[8];
var outputBlock = new byte[8];
using (var input = File.OpenRead(sourcePath))
using (var output = File.Open(destinationPath, FileMode.Create))
{
int offset = 0;
while (true)
{
var bytesRead = input.Read(inputBlock, 0, 8);
if (offset > 7)
{
var bytesToWrite = bytesRead == 0 ? (8 - outputBlock[7]) & 0xf : 8;
if (bytesToWrite > 0) { output.Write(outputBlock, 0, bytesToWrite); }
}
if (bytesRead == 0) break;
if (bytesRead != 8) throw new Exception("Corrupt input file");
for (int i = 0; i < bytesRead; i++)
{
outputBlock[i] = (byte)(XorBytes[offset++ % 16] ^ inputBlock[i] ^ previousInputBlock[i]);
previousInputBlock[i] = inputBlock[i];
}
}
}
}
public static void EncryptFile(string sourcePath, string destinationPath)
{
var inputBlock = new byte[8];
var previousInputBlock = new byte[8];
using (var input = File.OpenRead(sourcePath))
using (var output = File.Open(destinationPath, FileMode.Create))
{
int offset = 0;
while (true)
{
var bytesRead = input.Read(inputBlock, 0, 8);
if (bytesRead < 8)
{
byte paddingBytes = (byte)(8 - bytesRead);
byte padding = (byte)(paddingBytes | paddingBytes << 4);
for (int i = 0; i < paddingBytes; i++) { inputBlock[i + bytesRead] = padding; }
}
for (int i = 0; i < 8; i++)
{
inputBlock[i] = (byte)(XorBytes[offset++ % 16] ^ inputBlock[i] ^ previousInputBlock[i]);
previousInputBlock[i] = inputBlock[i];
}
output.Write(inputBlock, 0, 8);
if (bytesRead < 8) break;
}
}
}
}
}
"@;
Decrypt a backup
powershell
[Ms264556.Ruckus]::DecryptFile("C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.bak", "C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.bak.tgz")
Re-encrypt a backup
powershell
[Ms264556.Ruckus]::EncryptFile("C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.bak.tgz", "C:\Users\Ms264556\Downloads\ruckus_db_073122_14_17.modded.bak")