12.9. Vendor diagnostic, factory-test and provisioning tools in bin/xbin

artifacts/vendor_a/bin and artifacts/system_a/system/bin together hold several hundred native executables that had never been individually looked at in this project — only the .so HAL libraries and a handful of named tools had been. This page separates generic AOSP/toybox binaries from Qualcomm-vendor-specific ones with an actual per-file count, then disassembles the subset that could plausibly matter to a device with an unlocked/test-key-signed bootloader: diagnostic-port command surfaces, factory-test entry points, and provisioning tools that write into TZ-backed key storage.

Everything below is read statically from the shipped binaries in artifacts/vendor_a and artifacts/system_a. No code was executed, no device was connected, and no /dev/diag traffic was captured.

12.9.1. Inventory methodology and counts

artifacts/vendor_a/bin has 333 directory entries; artifacts/system_a/system/bin has 423. Neither number is “native executables” — most entries in both directories are 7z-extracted symlinks, and 7z represents a symlink not as a filesystem link but as a plain regular file whose entire content is the literal link target text (confirmed by hand: artifacts/vendor_a/bin/cat is a 13-byte regular file containing exactly the 13 ASCII bytes toybox_vendor, not an ELF header and not a real symlink inode). That extraction quirk is what makes a raw ls count meaningless and is the first thing the classification below controls for.

Counting by content (not by name) across both directories:

artifacts/vendor_a/bin            (333 entries)
  172  -> symlink-placeholder, target "toybox_vendor"   (AOSP toybox multicall)
    6  -> symlink-placeholder, target "toolbox"          (legacy AOSP multicall)
   23  -> real files, but shell scripts, not ELF          (init.{qcom,qti,mdm,...}.sh,
                                                             init.{keybox,erase_keybox,rpmb}.sh,
                                                             qca6234-service.sh, checkpoint_gc)
    2  -> directories misfiled alongside binaries          (hw/, qmi-framework-tests/)
  130  -> real ELF 64-bit AArch64 executables               <- the actual RE surface

artifacts/system_a/system/bin     (423 entries)
  ~233 -> symlink-placeholders/one-line wrapper scripts     (toybox x188, toolbox x6,
                                                              iptables/netutils-wrapper x10,
                                                              mke2fs x3, fsck.f2fs x3,
                                                              apex linker/linker64 x5,
                                                              ziptool x2, bzip2 x2, init,
                                                              app_process64, hwservicemanager,
                                                              ethtool, and five "cmd <x> \"$@\""
                                                              one-liners for window/settings/
                                                              package/input/appops)
  159  -> real ELF 64-bit AArch64 executables
   ~31 -> real shell scripts (recovery/init helpers, all stock AOSP)

The 130 real ELF executables in vendor_a/bin were then grepped by name for the standard Qualcomm/BSP vocabulary (diag, ftm, qmi, rpc, cnss, wcn, ims, qti/qcom, sensor, mdm/modem, persist, rmnet, factory, qsee, tz, hdcp, keybox, wigig/11ad, wfd) and by manual eyeballing of everything the grep missed. Of the 130:

  • 8 are stock AOSP/public tools, confirmed by strings/imports rather than name alone: awk and sh (AOSP’s standalone one-true-awk and mksh builds — separate binaries from, not symlinks to, toybox_vendor), toolbox and toybox_vendor themselves, dumpsys and logwrapper (plain AOSP framework/init helpers), and boringssl_self_test32 / boringssl_self_test64 (the public AOSP BoringSSL FIPS self-test binary, imports only libcrypto/standard libc symbols, no vendor libraries).

  • 122 are vendor/Qualcomm-specific by name and (for every one actually opened below) confirmed by their imported libraries and embedded strings — they link against libdiag.so, libqmi_cci.so, libnl.so + libwigig_ftm_flows.so, libkeymasterprovision.so, libhdcp1prov.so/libhdcp2p2prov.so, QSEECom APIs, or similar Qualcomm-only libraries that don’t exist on a generic AOSP device.

artifacts/system_a/system/bin (the Android-15-vintage GSI-like system half of this device’s The Onyx/Boox platform layer A/B split) turned up essentially nothing in the same keyword pass beyond generic AOSP components that merely contain a matching substring — iptables/ip6tables (netfilter, public), sensorservice, telecom, debuggerd, charger, recovery-persist — all stock AOSP binaries with public, documented behaviour. No Qualcomm-vendor-named native binary exists in system/bin on this device; the entire vendor-diagnostic/factory-test/provisioning surface lives in vendor_a/bin, consistent with the vendor/system version split described in The Onyx/Boox platform layer.

Of the 122 vendor-specific vendor_a/bin executables, 29 were opened and read (file + size + imported-symbol list + full strings dump, cross-checked against their exact .rodata string offsets where a claim below depends on it):

ftmtest  ftm_flows_test  myftm  mm-audio-ftm
diag_mdlog  diag_klog  diag_socket_log  diag_uart_log
diag_callback_sample  diag_dci_sample  test_diag  ssr_diag
fmfactorytest  fmfactorytestserver  fmconfig
vendor_cmd_tool  irsc_util
KmInstallKeybox  StoreKeybox  hdcp1prov  hdcp2p2prov
qseecomd  qseecom_sample_client  secure_ui_sample_client
vendor.qti.hardware.soter@1.0-provision  qwes_cli
athdiag  cnss_diag  sscrpcd  ssgtzd  dpmQmiMgr
feature_enabler_client

Five of those (fmfactorytest, fmfactorytestserver, ftmtest, KmInstallKeybox, hdcp1prov) were taken to full AArch64 disassembly with llvm-objdump -d plus manual ADRP/ADD string-literal resolution, because their strings suggested something worth confirming in code rather than trusting the string dump alone. Those five are the findings below.

The remaining ~93 vendor-named binaries (qrtr-ns/qrtr-cfg/qrtr-lookup, pd-mapper, netmgrd, ipacm/ipacm-diag, wpa_cli, hostapd_cli, loc_launcher/lowi-server/xtra-daemon/ xtwifi-client, wigighalsvc/wigig_wiburn/wigignpt, wfdvndservice/wfdhdcphalservice/wifidisplayhalservice, thermal-engine, msm_irqbalance, power_off_alarm, poweropt-service, rmt_storage, subsystem_ramdump, ssr_setup, time_daemon, tloc_daemon, npu_launcher, nn_device_test, nativechitest/nativehaltest, chifeature2test/chiofflinepostproctest, and the ims*/imsrcsd family, among others) are Qualcomm reference-platform daemons and test harnesses for radio/IPA/location/thermal/display subsystems whose names, sizes and spot-checked library dependencies are consistent with their apparent public purpose (QMI/QRTR IPC plumbing, IMS/RCS daemons, WLAN location, thermal mitigation, NPU inference test harness, camera HAL conformance test binaries). They were not individually disassembled — there is no indication in their names or on-disk footprint that they differ from well-documented Qualcomm BSP components; individual entries for all 93 are omitted below rather than padding this page with unverified assumptions.

12.9.2. Findings

12.9.2.1. FM radio factory test

fmfactorytest (16,024 bytes) is a tiny CLI client; fmfactorytestserver (24,608 bytes) is the daemon it talks to over /dev/socket/fm_factory_socket_server (string at file offset 0x9cc in fmfactorytest’s .rodata). Full disassembly of fmfactorytest’s single .text blob (file offset 0x10000x15dc, no function symbols — the binary exports/imports only PLT stubs) shows:

0x1098  add   x1, x1, #0xa2b      ; "enable"   (argv[1] compared via strcmp)
0x10b4  add   x1, x1, #0x8f0      ; "disable"
0x1198  add   x1, x1, #0x92d      ; "tune"
0x120c  add   x1, x1, #0xa32      ; "seeknext"

argv[1] must be exactly one of those four verbs. If the client can’t connect() to the factory socket (0x11500x1164), it does not just fail — it forks and, in the child, calls ``system()`` naming the server binary itself:

0x1268  bl    fork
0x126c  cbz   w0, 0x12d0          ; child branch
...
0x12d0  adrp  x0, 0x0
0x12d4  add   x0, x0, #0x96b      ; "fmfactorytestserver"
0x12d8  bl    system
0x12dc  mov   w20, w0
0x12e0  b     0x1188              ; back to log + continue

the parent retries connect() up to 10 times with a ~15.7 ms (0x3d40 = 15680) usleep() between attempts (0x12780x12c0) before giving up. So invoking fmfactorytest with any recognised verb is sufficient, on its own, to spawn fmfactorytestserver via a bare system("fmfactorytestserver") call if it isn’t already running — no absolute path, relying entirely on $PATH. This is unremarkable as a factory tool (inferred: likely invoked by test harnesses that already control the environment, not confirmed). But it is a concrete, address-verified example of this device’s diagnostic tooling using fork() + system() with a bare (non-absolute) command name, which is exactly the pattern that matters if $PATH/environment trust ever becomes attacker-influenced in some other context on this device.

The server side, once connected, accepts the same four verbs as colon-prefixed command strings, and drives them straight into the V4L2 kernel radio driver. strings on fmfactorytestserver shows /dev/radio0 (offset 0x11fd) opened, and disassembly around the two ioctl() call sites at file offsets 0x2388 and 0x23b8 shows the request code built as:

0x236c  mov   w1, #0x561c
0x2380  movk  w1, #0xc008, lsl #16   ; w1 = 0xc008561c
0x2384  str   x8, [sp, #0x30]        ; {control_id, value} = {0x08000003, 0x0e}
0x2388  bl    ioctl

0xc008561c decodes as the standard V4L2 _IOWR('V', 28, ...)VIDIOC_S_CTRL (an 8-byte struct v4l2_control{__u32 id; __s32 value;}, matching the 0x08 size field) — i.e. this is a plain V4L2 tuner/radio control-set ioctl, not anything proprietary. The FM factory-test path is, in the call sites traced here, a userspace shim over the standard Linux V4L2 radio API, with no proprietary or hidden logic found.

12.9.2.2. WLAN “FTM” tools

ftmtest (34,344 bytes) and ftm_flows_test (28,568 bytes) sit right next to the Qualcomm-diag family’s ftmdaemon/ftm_flows_test naming convention (where “FTM” = Factory Test Mode, the RF calibration mode most Qualcomm phones can be booted into), but are unrelated to it. Their imports (nl_socket_*, nla_*, genl_* from libnl.so; aoa_init/aoa_process from a private libaoa.so; wigig_ftm_flows_* from libwigig_ftm_flows.so) and usage strings ("ftm <mac_addr> [<count> <delay> [<timeout>]]: perform single-shot FTM measurement", "aoa <mac_addr> ...: perform AOA measurement") show these are WLAN location test tools: they drive the 802.11mc Fine Timing Measurement protocol and Angle-of-Arrival ranging over nl80211 vendor commands, for indoor-positioning RF calibration — an entirely different “FTM” than the Qualcomm diag/factory-test one. Naming collision, confirmed in code, not a shared code path.

ftmtest’s command dispatch (no function symbols; offsets below are file offsets within its single .text) does four back-to-back strcmp() calls against the second command-line argument:

0x52d0  add   x1, x1, #0x831   ; "capa"     -> cbz -> 0x536c (table slot +0x00)
0x52ec  add   x1, x1, #0x742   ; "ftm"      -> cbz -> 0x5358 (table slot +0x20)
0x5300  add   x1, x1, #0x7fe   ; "aoa"      -> cbz -> 0x5360 (table slot +0x40)
0x5314  add   x1, x1, #0x8c7   ; "ftmaoa"   -> cbz -> 0x5368 (table slot +0x60)
0x5328  ...                    ; none matched -> "unknown command: %s", exit

each match selects one of four 0x20-byte-stride entries in a static table (base x7000+0xd0), and the actual handler is reached through an indirect call, blr x20 at file offset 0x53c4, after a small range check (0x53940x53b0) that looks like an LLVM CFI jump-table guard rather than application logic (it pairs with the __cfi_slowpath/__cfi_check imports present throughout this binary — the whole vendor_a build appears to be CFI-instrumented, consistent with Security and DRM userspace’s description of this device’s hardening baseline). capa/ftm/aoa/ftmaoa dispatch to “show location capabilities”, single-shot FTM ranging, AOA measurement, and combined FTM+AOA respectively — all consistent with the usage strings and with nothing that looks like a hidden fifth command.

12.9.2.3. Attestation-keybox provisioning tool (KmInstallKeybox)

KmInstallKeybox (67,944 bytes) links libkeymasterprovision.so, libqtikeymaster4.so, libcrypto.so (X509/EVP calls) and libqmi_cci.so, and exports the mangled C++ symbol keymasterdevice::KeymasterKeyProvision::KeyMasterInstallKeybox(km_install_keybox_t*). Per its own embedded usage text (which the disassembly below confirms is exactly, and only, printed by the argument-count-mismatch path — this is documentation, not enforced logic in this binary):

KmInstallKeybox will install the attestationkeys (both RSA and ECC) along
with all the device identifiers (brand, device, product, serial#, IMEI,
MEID (if available), model and manufacturer). [...] Once 'Provisioning
Success' state is set in KM TA / Strongbox, attestation keys / Device
ID's cannot be re-provisioned

**RMA Use Case **
For RMA use case, OEM's can set the following devcfg parameter in
'keymaster_oem_config.xml'
Please ensure to sign this debug devcfg with the serial# of the device as
this will enable re-provisioning of keys and if used incorrectly can open
up the device to be re-provisioned.
<props name="allow_reprovision" type=DALPROP_ATTR_TYPE_UINT32>

usage: LD_LIBRARY_PATH=/vendor/lib64/hw KmInstallKeybox Keybox_file Device_ID provision_device_ids(true/false) Strongbox_Keybox_file Strongbox_Device_ID strongbox_provision_device_ids(true/false)

Disassembly confirms this text (three separate literals at file offsets 0x37c3, 0x332a, 0x3e2e in .rodata — scattered inside a much larger, heterogeneous rodata blob, with roughly 3.3 KB of interleaved, unrelated Keymaster strings between them (KM_TAG_* names, error messages like “SW enforced failed”, “Meid: “, “SerialNum: “, etc.), not one contiguous help banner) is only reachable from the argc-mismatch path, printed via three back-to-back calls at file offsets 0xaae0, 0xaaf0 and 0xab00 into an internal puts-style wrapper at 0x5388, immediately followed by mov w0, #-0x1; bl exit at 0xab100xab14. This is Qualcomm’s own documentation of a device-identity re-provisioning mechanism, gated (per that same text) by a signed devcfg property rather than by anything in this userspace binary — the signature check, if enforced, lives in the Keymaster TA, which this static pass did not reach (see Honest limits). It is reported here because it is a factory/RMA-only capability to re-provision attestation identity and key material, shipped, unremoved, in a consumer retail image, and worth knowing about for anyone reasoning about this device’s attestation trust boundary — not because a working bypass was demonstrated.

StoreKeybox (33,432 bytes, UBSan-instrumented, plain ifstream-based) is the sibling tool that reads a keybox file from disk; it was string- and import-level examined but not disassembled — its behaviour (open file, parse, store) is unremarkable relative to KmInstallKeybox and adds nothing this page didn’t already cover.

12.9.2.4. HDCP1 key-provisioning tool (hdcp1prov)

hdcp1prov (11,648 bytes) is small enough to disassemble in full. It links only libhdcp1prov.so, libdiag.so and liblog.so, and exports nothing beyond the CRT stubs — all logic funnels through two imports, HDCP1_key_provision and HDCP1_key_verify. Reading main (file offset 0x106c0x130c):

0x1088  ldr  x0, [x19, #0x8]        ; argv[1]
0x1090  add  x1, x1, #0x839         ; "-verify"
0x1094  bl   strcmp
0x1098  cbz  w0, 0x10c4             ; argv[1]=="-verify" (and argc==2) -> verify path
...
0x119c  bl   HDCP1_key_verify       ; called with w0=1 (a fixed "type")

; --- provisioning path (argv[1] != "-verify") ---
0x1114  ldr  x0, [x19, w25, uxtw #3]
0x1118  bl   atoi                   ; "type" = atoi(argv[last])
0x111c  sub  w8, w0, #0x1
0x1124  cmp  w8, #0x3
0x1128  b.hs 0x127c                 ; reject unless type in {1,2,3,4}
...
0x1144  ldr  x0, [x19, #0x8]        ; argv[1] = KEY_FILE
0x1150  bl   fopen                  ; mode string at offset 0x837 ("r")
0x1170  bl   fread_unlocked         ; up to 1000 bytes into a static buffer
...
0x11f0  ldr  x0, [x19, #0x10]       ; argv[2] = DPS_FILE
0x11fc  bl   fopen                  ; mode string at offset 0x781 ("rb")
0x1214  bl   fread_unlocked         ; exactly 16 bytes (a "DPS" seed)
0x1218  cmp  x0, #0x10
0x121c  b.ne 0x12fc                 ; must read exactly 16 bytes
0x1238  bl   HDCP1_key_provision    ; (keybuf<=1000B, type 1..4, dps[16], 16)

There is no signature check, no hash comparison, no authentication of either input file anywhere in this binary — it reads whatever bytes are at the given paths (up to 1000 bytes of key material, exactly 16 bytes of “DPS”, and an attacker/operator-chosen small integer selecting one of four HDCP1 key “types”) and hands them straight to HDCP1_key_provision() in libhdcp1prov.so. Whatever cryptographic gate exists between that library call and actually writing HDCP1 device keys into secure storage is inside libhdcp1prov.so and/or the TrustZone/QSEE side it may call into (unconfirmed) — static analysis did not follow that library (see Honest limits). hdcp2p2prov (11,640 bytes) is the HDCP2.2 sibling with the identical structure (HDCP2P2_key_provision/HDCP2P2_key_verify); it was string- and import-level examined but not separately disassembled since the pattern is the same.

12.9.3. Honest limits

  • The security-relevant gating for both the keybox-reprovisioning path and the HDCP key-provisioning path was not located. For KmInstallKeybox, the “sign the devcfg with the device serial#” check its own help text describes is not implemented in this binary (it just prints the help text) — it must live in the Keymaster TA (libqtikeymaster4.so / the in-TZ keymaster trustlet), which is a stripped shared library, and further in a secure-world trustlet image, neither of which was disassembled here. For hdcp1prov/hdcp2p2prov, the equivalent question — whether HDCP1_key_provision() inside libhdcp1prov.so performs any authentication before writing to secure storage, or whether that responsibility is pushed even further down into TZ — was not answered; libhdcp1prov.so was not disassembled.

  • Whether any of these binaries is actually reachable by an unprivileged or even an ``adb shell`` caller on this device was not determined. No SELinux policy (vendor/etc/selinux) or executable permission bits were cross-checked; several of these look, from their names and “for RMA use” documentation, like factory-line-only tools that would normally be gated by SELinux domain transitions and/or physical factory-jig-only invocation, not general userspace reachability. This page establishes what the binaries do if invoked, not who can invoke them.

  • 93 of the 122 vendor-specific ``vendor_a/bin`` executables were classified by name/library/size only, not opened. The 29 that were opened (listed above) are the ones whose names indicate diagnostic, factory-test, or key-provisioning functionality; nothing found suggests the unopened remainder contains something those 29 don’t already represent, but that is an inference from naming convention, not a verified claim about each of those 93 files.

  • No live device was used anywhere in this investigation; every claim above is read directly from the static ELF images.

12.9.4. Provenance

Source:

artifacts/vendor_a/bin/{fmfactorytest,fmfactorytestserver,ftmtest, ftm_flows_test,myftm,mm-audio-ftm,diag_mdlog,diag_klog,diag_socket_log, diag_uart_log,diag_callback_sample,diag_dci_sample,test_diag,ssr_diag, fmconfig,vendor_cmd_tool,irsc_util,KmInstallKeybox,StoreKeybox,hdcp1prov, hdcp2p2prov,qseecomd,qseecom_sample_client,secure_ui_sample_client, vendor.qti.hardware.soter@1.0-provision,qwes_cli,athdiag,cnss_diag, sscrpcd,ssgtzd,dpmQmiMgr,feature_enabler_client} and the full artifacts/vendor_a/bin (333 entries) / artifacts/system_a/system/bin (423 entries) directory listings, all extracted read-only with 7z from artifacts/super/vendor_a.img and artifacts/super/system_a.img respectively (ext2/ext4 images extracted with 7z x).

Method:

Directory classification done by reading raw file content/size (not trusting ls) to separate 7z’s symlink-as-text-file artifacts from real ELF binaries, then keyword-grepping and manually eyeballing the remainder. Per-binary triage used file, byte-size, and a macOS-strings (strings -n 6) dump of each candidate. The five deep-dived binaries were fully disassembled with the pixi llvm-objdump -d --no-show-raw-insn (AArch64, resolves imported-symbol names at PLT stubs directly since these are small non-stripped-import executables with intact .dynsym/section headers, unlike the large stripped .so files documented elsewhere in this project). Every .rodata string cited by file offset was independently confirmed with a short Python script reading the raw bytes at that offset out of the binary — not read off the strings dump positionally, since strings does not report offsets by default. The project’s existing artifacts/re_static/{sodis,elfdis}.py capstone helpers were evaluated but not used for the final excerpts above (they are built for stripped, section-header-light .so files; these binaries have full section headers and dynamic symbol tables, so vanilla llvm-objdump -d resolves PLT call targets more directly).

Cross-refs:

Security and DRM userspace (QSEECOM/TrustZone bridge, attestation keybox and HDCP/ Widevine provisioning this page’s KmInstallKeybox/hdcp1prov/ hdcp2p2prov findings sit underneath), The Onyx/Boox platform layer (the vendor/system version split that puts the entire vendor-diagnostic surface found here on the vendor_a side and none of it on system_a), Userspace boot — init, SELinux, A/B updates (SELinux domain context these binaries would run under, not independently verified here).