Posts Foxconn FCC Unlock
Post
Cancel

Foxconn FCC Unlock

Many newer Dell notebooks are equipped with DW5932e WWAN modules for mobile connectivity. These modules are manufactured by Foxconn and contain a Snapdragon X62 5G modem. Unfortunately, at the time of writing this blog post, these modules do not yet work with ModemManager on Linux, meaning that mobile web is not possible. However, since this is crucial for the mobile operation of our notebooks, it was time to investigate this issue and find or develop a solution.

FCC lock

The reason for these modules not working is the FCC lock, which is a software restriction included in most WWAN modules shipped by various notebook manufacturers. To allow the module to go online, an usually undocumented command must be sent to the module to unlock it. The manufacturers claim this lock exists, so the FCC, which is the authority for certifying radio-enabled devices in the United States, can certify both notebook and modem at the same time. More information about the FCC lock are available in the ModemManager documentation.

FoxFlss

After looking through some Dell forums, several people had success using the module under Linux with a repository called fii_linux “Foxconn Linux application” hosted on GitHub. This repository belongs to an account called foxconn-pc. Whether this account is actually operated by Foxconn is unclear, but it seems unlikely considering that this repository is the only project on the account. The repository contains some encrypted binary blobs and a tool called FoxFlss, which claims to be able to unlock the FCC lock and write “RF_Files” to the modem. A script for ModemManager is provided, which will use the FoxFlss binary to unlock the FCC lock. Additionally, a systemd service is provided which will write the “RF_Files” to the modem.

To ensure this repository doesn’t contain any malicious code, we loaded the FoxFlss binary into ghidra to perform static analyses. The binary includes debug symbols with functions names, which makes reverse engineering a lot easier. The part which decrypts the binary blobs was quickly found and it’s a simple XOR encryption with a static repeating byte.

The decompiled function used for de- and encryption The decompiled function used for de- and encryption

After decryption, the blobs were .tar.gz archives which then can be extracted using the tar utility. The extracted archive contains additional binary blobs and configuration values which are loaded into the modules NVRAM by the FoxFlss binary.

Analyzing the Windows driver

In order to ensure that none of the binaries from FoxFlss are malicious, we started looking into the Windows driver. Assuming this is an official binary by the manufacturer, it is likely that the Windows driver contains similar files which get written to the module. We extracted the .exe downloaded from the Dell website, and one of the files looked interesting. Looking at the debug strings of one of the files called SIMService.exe reveals that it contains similar logs as the FoxFlss binary and might also perform the FCC unlocking sequence and the mysterious RF file writing. Reverse engineering the executable in ghidra confirms that it indeed also writes RF files to the module, which are contained in the executable’s resources.

Log function in the Windows driver's SIMService.exe Log function in the Windows driver’s SIMService.exe

Log function in the linux FoxFlss binary Log function in the Linux FoxFlss binary

Using wrestool, these resources can be listed and extracted directly from the executable:

1
2
3
4
5
6
7
8
9
$ wrestool -l extracted/Production/Windows10-x64/17134/Drivers/SIMService/SIMService.exe
--type='EDL_UPGRADE' --name=112 --language=2052 [offset=0xf8148 size=322631]
--type='RF_FILE' --name=109 --language=2052 [offset=0xae210 size=287275]
--type='TUNER_DISABLE' --name=111 --language=2052 [offset=0xf4440 size=15623]
--type='TXT' --name=103 --language=1028 [offset=0x147058 size=2617]
--type=16 --name=1 --language=1028 [type=version offset=0x146d90 size=708]
--type=24 --name=1 --language=1033 [offset=0x147a98 size=392]

$ wrestool -x extracted/Production/Windows10-x64/17134/Drivers/SIMService/SIMService.exe --raw -o SIMService_resources/

Extracting the RF_FILE resource reveals a simple .zip file which after extraction is almost identical to the RF files written by the Linux FoxFlss binary. The Windows driver only has some additional files. Why these files were encrypted in the Linux binary in the first place is unclear.

1
2
3
4
5
6
7
$ diff -qr RF_Files_windows/ ../../DW5932e/RF_Files/
Only in RF_Files_windows/: 0C64
Only in RF_Files_windows/: 0CB6
Only in RF_Files_windows/: 0CBE
Only in RF_Files_windows/: 0CC2
Only in RF_Files_windows/: 0CC6
Only in RF_Files_windows/: 0CD3

Unlocking the FCC lock

To perform the FCC unlocking sequence on Linux, ModemManager bundles several scripts for various modems. One of these scripts is for the Foxconn SDX55 chip, where the unlocking seems really similar to the SDX62 in our modem we are interested in. The unlocking sequence itself creates a MD5 hash based on the modem firmware versions, IMEI and a magic value and additionally a random generated salt, to ensure the hashes will be different on every unlock. Comparing the SDX55 hash generation to the decompiled FoxFlss binary and the Windows driver reveals that the only difference is the magic value. The magic value for the SDX55 is foxc, while the new magic value is now FDE1. Unfortunately, even after adjusting this value, the FCC unlock still didn’t work.

1
2
3
4
5
6
SALT="salt" # use a static salt for now
MAGIC="foxc"
HASH="${SALT}$(printf "%s%s%s%s%s" "${FIRMWARE_VERSION}" \
    "${FIRMWARE_APPS_VERSION}" "${IMEI}" "${SALT}" "${MAGIC}" \
    | md5sum \
    | head -c 32)"

In order to send these unlock commands to the module, qmicli is used. qmicli is a tool which can send QMI (Qualcomm MSM Interface) messages to the module from the command line. A QMI message contains a message ID and is sent to a specific QMI service. To unlock the FCC lock, the SDX55 script uses the qmicli argument --dms-foxconn-set-fcc-authentication-v2 that sends a message with ID (0x5571), which appears to be the same message ID the FoxFlss binary uses. The DMS prefix here indicates that this message is sent to the DMS service (0x02). Looking at the FoxFlss decompilation again reveals that this is the important difference. FoxFlss and the Windows driver send the message to the FOX service (0xE3) instead. So it looks like they moved this command into their own service between the SDX55 and SDX62. After adding a new argument for this service to libqmi and adjusting the script, the unlocking is now working without any issues.

Decompiled function in the FoxFlss binary performing the FCC unlock Decompiled function in the FoxFlss binary performing the FCC unlock

Conclusion

We opened a merge request !417 in the libqmi repo, which was merged and is now part of the latest 1.37.1-dev development release. This adds the fox-set-fcc-authentication argument which will send the FCC authentication data to the FOX service. An unlock script based on the original SDX55 script for ModemManager using this new argument can be found below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
84
85
#!/bin/sh

# SPDX-License-Identifier: CC0-1.0
# 2021 Aleksander Morgado <aleksander@aleksander.es>
# 2022 Thilo-Alexander Ginkel <thilo@ginkel.com>
# 2022 Bjørn Mork <bjorn@mork.no>
# 2025 Jan Wütherich <jan.wuetherich@syss.de>
#
# Foxconn SDX62 FCC unlock operation
#

# require program name and at least 2 arguments
[ $# -lt 2 ] && exit 1

# first argument is DBus path, not needed here
shift

# second and next arguments are control port names
for PORT in "$@"; do
  # match port type in Linux 5.14 and newer
  grep -q MBIM "/sys/class/wwan/$PORT/type" 2>/dev/null ||
  # match port name in Linux 5.13
  echo "$PORT" | grep -qi MBIM && {
    MBIM_PORT="$PORT"
    break
  }
done

# fail if no MBIM port exposed
[ -n "$MBIM_PORT" ] || exit 2

log_v2_failure() {
  echo "$1" >&2
}

FIRMWARE_VERSION=$(qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \
  --fox-get-firmware-version=firmware-mcfg \
  | grep "Version:" \
  | grep -o "'.*'" \
  | sed "s/'//g" \
  | sed -e 's/\.[^.]*\.[^.]*$//')

if [ -n "${FIRMWARE_VERSION}" ]; then
  FIRMWARE_APPS_VERSION=$(qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \
    --fox-get-firmware-version=apps \
    | grep "Version:" \
    | grep -o "'.*'" \
    | sed "s/'//g")

  if [ -n "${FIRMWARE_APPS_VERSION}" ]; then
    IMEI=$(qmicli --device-open-proxy --device="/dev/$MBIM_PORT" --dms-get-ids \
      | grep "IMEI:" \
      | grep -o "'.*'" \
      | sed "s/'//g")

    if [ -n "${IMEI}" ]; then
      SALT="$(LC_ALL=C tr -dc a-z0-9 < /dev/urandom | head -c 4)"
      MAGIC="FDE1"
      MD5=$(printf "%s%s%s%s%s" "${FIRMWARE_VERSION}" \
        "${FIRMWARE_APPS_VERSION}" "${IMEI}" "${SALT}" "${MAGIC}" \
        | md5sum \
        | head -c 32)
      HASH="${SALT}${MD5}"
    else
      log_v2_failure "Could not determine SDX62 IMEI"
    fi
  else
    log_v2_failure "Could not determine SDX62 firmware apps version"
  fi
else
  log_v2_failure "Could not determine SDX62 firmware version"
fi

UNLOCK_RESULT=1
if [ -n "${HASH}" ]; then
  qmicli --device-open-proxy --device="/dev/$MBIM_PORT" \
    --fox-set-fcc-authentication="${HASH},48"
  UNLOCK_RESULT=$?

  if [ $UNLOCK_RESULT -ne 0 ]; then
    log_v2_failure "SDX62 FCC unlock failed"
  fi
fi

exit $UNLOCK_RESULT

At the time of writing this blog post, it is not clear what the exact purpose of the RF files is, as the modem appears to be functioning without any issues without providing them.

This post is licensed under CC BY 4.0 by the author.