Created
December 11, 2023 04:33
-
-
Save marcnewlin/c936d1530fb5e3648aca23d7c08c9628 to your computer and use it in GitHub Desktop.
script to extract firmware images from Alienware AW920K update tools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
''' | |
Firmware updates for the Alienware AW920K keyboard (and dongle) are encrypted | |
using a key derived from the size of the update. | |
This tool extracts the unencrypted firmware images from the update packages. | |
Instructions: | |
1. Download the Alienware AW920K firmware updates (dongle and keyboard): | |
https://www.dell.com/support/home/en-us/drivers/driversdetails?driverid=py9mn | |
https://www.dell.com/support/home/en-us/drivers/driversdetails?driverid=xff5j | |
2. Place the update packages in the same directory as this script | |
> ls | |
AW920K_Dongle_v0051.exe AW920K_Keyboard_v0051.exe extract-firmware.py | |
3. Run the script | |
> ./extract-firmware.py | |
success! wrote 75876 bytes to dongle.bin | |
success! wrote 66400 bytes to keyboard.2G4.bin | |
success! wrote 109856 bytes to keyboard.BLE.bin | |
success! wrote 109952 bytes to keyboard.USB.bin | |
4. The decrypted firmware images will be saved in the current directory | |
> ls *.bin | |
dongle.bin keyboard.2G4.bin keyboard.BLE.bin keyboard.USB.bin | |
''' | |
import binascii | |
import crcmod | |
import re | |
import struct | |
from Crypto.Cipher import AES | |
def read_ini(raw, name): | |
m = re.search(b"%s=([a-fA-F0-9]+)" % name.encode(), raw) | |
if m is None: return m | |
return m.group(1) | |
def decrypt(blob, fw_len=None): | |
if fw_len is None: | |
fw_len = len(blob) | |
seed = struct.pack(">IIII", fw_len, fw_len, fw_len, fw_len) | |
ecb = AES.new(seed, AES.MODE_ECB) | |
key = ecb.encrypt(seed) | |
cbc = AES.new(key, AES.MODE_CBC, iv=key) | |
return cbc.decrypt(blob) | |
def pad(chunk): | |
if len(chunk) % 16 == 0: | |
return chunk | |
else: | |
chunk += b"\x00"*(16-(len(chunk)%16)) | |
return chunk | |
def extract_AW920K_Dongle_v0051_exe(): | |
# load the updater binary | |
with open("AW920K_Dongle_v0051.exe", "rb") as f: | |
data = f.read() | |
# read the firmware length | |
fw_len = int(read_ini(data, "INI_FW_LEN")) | |
fw_len_not_padded = fw_len | |
if fw_len % 16 != 0: | |
fw_len = fw_len - (fw_len % 16) + 16 | |
# read the expected firmware crc | |
crc_given = read_ini(data, "INI_FW_CRC") | |
crc_given = struct.unpack(">I", binascii.unhexlify(crc_given))[0] | |
# extract and decrypt the firmware image | |
magic = b"\x00\x00\x00\x00IEND\xAE\x42\x60\x82" | |
ix = data.find(magic) + len(magic) | |
fw_enc = data[ix:ix+fw_len] | |
fw_dec = decrypt(fw_enc, fw_len)[:fw_len_not_padded] | |
# validate crc and save the image to disk | |
crc32 = crcmod.mkCrcFun(0x104C11DB7, 1, True, 0xffffffff) | |
crc = crc32(fw_dec) | |
if crc != crc_given: | |
print("CRC check failed (expected=0x%08x, actual=0x%08x)" % (crc_given, crc)) | |
else: | |
with open("dongle.bin", "wb") as f: | |
f.write(fw_dec) | |
print("success! wrote %d bytes to %s" % (len(fw_dec), "dongle.bin")) | |
def extract_AW920K_Keyboard_v0051_exe(): | |
# load the updater binary | |
with open("AW920K_Keyboard_v0051.exe", "rb") as f: | |
data = f.read() | |
names = ["2G4", "BLE", "USB"] | |
# read the firmware lengths | |
lengths = [int(read_ini(data, "INI_%s_FW_SIZE"%n)) for n in names] | |
for x in range(len(names)): | |
if lengths[x] % 16 != 0: | |
lengths[x] += (16 - (lengths[x] % 16)) | |
# read the expected checksums | |
checksums = [int(read_ini(data, "INI_%s_FW_CHKSUM"%n), 16) for n in names] | |
# extract the encrypted firmware blobs | |
magic = b"\x00\x00\x00\x00IEND\xAE\x42\x60\x82" | |
ix = data.find(magic) + len(magic) | |
for x in range(len(names)): | |
chunk = data[ix+sum(lengths[:x]):ix+sum(lengths[:x+1])] | |
fw_dec = decrypt(chunk) | |
path = "keyboard.%s.bin" % names[x] | |
# validate checksum and save the image to disk | |
cksum = sum(fw_dec) | |
if cksum != checksums[x]: | |
print("bad checksum (expected=0x%08x, actual=0x%08x)" % (checksums[x], cksum)) | |
else: | |
with open(path, "wb") as f: | |
f.write(fw_dec) | |
print("success! wrote %d bytes to %s" % (len(fw_dec), path)) | |
if __name__ == "__main__": | |
extract_AW920K_Dongle_v0051_exe() | |
extract_AW920K_Keyboard_v0051_exe() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment