Skip to content

Instantly share code, notes, and snippets.

@up-n-atom
Last active July 15, 2025 08:10
Show Gist options
  • Select an option

  • Save up-n-atom/8c55913a8bf206596d1366a5fa935acf to your computer and use it in GitHub Desktop.

Select an option

Save up-n-atom/8c55913a8bf206596d1366a5fa935acf to your computer and use it in GitHub Desktop.
HALNy HLX-SFPX Firmware Tool

Based on reverse engineering /usr/lib/libponimg.so

git clone https://github.com/xxtea/xxtea-c
cd xxtea-c
cmake .
make
cd ..
gcc -static -I./xxtea-c -L./xxtea-c -o test test_decrypt.c -lxxtea
./test
#!/usr/bin/python3
import argparse
import os
import sys
from typing import BinaryIO
import xxtea
BLOCK_LENGTH = 0x8000
MIN_BYTE_VALUE, MAX_BYTE_VALUE = 0, 255
HLX_SFPX_KEY = b'q!9RbM0Dn@Mq5hPb' # found in /usr/lib/libponimg.so
# HL-1B keys are encoded with a Caesar cipher
# dec_key = ''.join([chr((c + 16) % 256) for c in enc_key])
HL_1B_TR69_KEY = b'15gUPh^sw9@_kfgM' # encoded as !%WE@XNcg)0O[VW= in /userfs/bin/tr69
HL_1B_OMCI_KEY = b'fia9=RZeB1BL6f=e' # encoded as VYQ)-BJU2!2<&V-U in /lib/libapi_omci_adpt.so and /lib/libcfg_clisvc.so
class ListKeyAction(argparse.Action):
def __init__(self, option_strings, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, help=None):
super().__init__(option_strings=option_strings, dest=dest, default=default, nargs=0, help=help)
def __call__(self, parser, namespace, values, option_string=None):
print(*[f"{key:>15}: {value.decode('ascii')}" for key, value in globals().items() if key.endswith('KEY')], sep=os.linesep)
parser.exit()
def decrypt32k(in_stream: BinaryIO, out_stream: BinaryIO, dec_key: bytes) -> None:
while pad_dat := in_stream.read(1):
if (enc_dat := in_stream.read(BLOCK_LENGTH + int.from_bytes(pad_dat))) and len(enc_dat):
dec_dat = xxtea.decrypt(enc_dat, dec_key)
if len(dec_dat):
out_stream.write(dec_dat)
else:
raise BufferError('Decrypted data error')
def encrypt32k(in_stream: BinaryIO, out_stream: BinaryIO, enc_key: bytes) -> None:
while (dec_dat := in_stream.read(BLOCK_LENGTH)) and len(dec_dat):
enc_dat = xxtea.encrypt(dec_dat, enc_key)
pad_len = len(enc_dat) - len(dec_dat)
if MIN_BYTE_VALUE < pad_len <= MAX_BYTE_VALUE:
out_stream.write(pad_len.to_bytes(1))
out_stream.write(enc_dat)
else:
raise ValueError('Encrypted data length out of range')
def genkey(key: str) -> bytes:
return f"{key[:16]:<16}".encode('ascii')
def main() -> None:
parser = argparse.ArgumentParser(description='HALNy HLX-SFPX Firmware Tool')
parser.add_argument('-l', '--list', action=ListKeyAction, help='list known keys')
parser.add_argument('-k', '--key', default=HLX_SFPX_KEY, type=genkey, help='128-bit key')
parser.add_argument('-e', '--encrypt', action='store_true')
parser.add_argument("infile", type=argparse.FileType('rb'))
parser.add_argument("outfile", type=argparse.FileType('wb'))
args = parser.parse_args()
try:
with args.infile as in_file, args.outfile as out_file:
crypt = encrypt32k if args.encrypt else decrypt32k
crypt(in_file, out_file, args.key)
except Exception as err:
os.remove(args.outfile.name)
sys.exit(f"Error: {err}")
else:
sys.exit(0)
if __name__ == '__main__':
main()

Linux & OS X

python3 -m venv .venv
source .venv/bin/activate # source .venv/bin/activate.fish
pip3 install -r requirements.txt

Decrypt

halny_crypt.py input.bin output.bin

Encrypt

halny_crypt.py -e input.bin output.bin

Specify another key

Keys are 16 chars and will be truncated if longer and padded with spaces if shorter

halny_crypt.py -k 'This is a test' input.bin output.bin
cffi
pycparser
setuptools
xxtea-py
#include <stdio.h>
#include <stdlib.h>
#include "xxtea.h"
#define KEY "q!9RbM0Dn@Mq5hPb"
int main(){
FILE *fin, *fout;
int c;
unsigned char *dec, enc[0x8100];
size_t elen, dlen;
fin = fopen("G_ONU_HLX_SFPX_V7-0-6t1-e.bin", "rb");
fout = fopen("firmware.bin", "wb");
while ((c = fgetc(fin)) != EOF) {
elen = fread(enc, 1, c + 0x8000, fin);
dec = xxtea_decrypt(enc, elen, KEY, &dlen);
fwrite(dec, 1, dlen, fout);
free(dec);
}
fclose(fin);
fclose(fout);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment