I took HP Smart apart to find out what it really says to my printer
I have an HP ENVY 6000 on my desk. To use everything it can do from Windows I need HP Smart, which wants an HP account, a cloud connection and a few permissions I would rather not hand out. The printer is on my LAN. The laptop is on my LAN. Why is there a cloud in between?
So I unpacked the app and looked at what it actually sends to the printer. The short answer: almost everything useful is plain IPP and HTTP/XML. That turned into hpfree, a Rust CLI plus a tiny local web UI that prints, scans, reads ink levels and runs the service tools without an account.
Step one: the app is not what I expected
HP Smart is a UWP app, installed from the Store as an MSIX bundle (591 MB, three architectures and 35 language packs). I assumed I would throw ILSpy at it and read C#.
PE file does not contain any managed metadata
HP.Smart.dll is 152 MB and compiled with .NET Native, so it is ahead-of-time compiled x64. There is no IL and no PDB, and none of the usual .NET decompilers will open it. The same goes for HPScan.dll, HPPageLift.UWP.dll and all 40 HP.Framework.Extensions.* plugins. Reading that code properly means Ghidra and a long weekend. I installed Ghidra 12 and queued the import, and as I write this the native analysis has not finished. Everything below comes from the parts that did open.
What did open
- The
DesktopExtensionfolder has 55 managed DLLs, about 700k lines of C# once decompiled withilspycmd. This isHP.Jarvis.*, HP’s internal microservice-style framework:Auth,Discovery,DeviceSvcs,SwaggerSvcs,DataCollectionand so on.DeviceSvcsalone is 130k lines. - The
.winmdfiles gave me the WinRT contracts and the background tasks (telemetry, print support, toast notifications). stringson the native DLL gave 1,757 URLs, 2,943 class names and the list of cloud hosts the app is allowed to talk to.resources.prigave 73k UI strings. The XAML layouts are compiled to XBF2 and cannot be recovered, but the resource names tell you what screens exist.hpuiodatamap.xml, a 148 KB file sitting next to the exe, was the best find. It maps 224 UI elements to printer attributes, so it is effectively a table of contents for what HP Smart asks a printer.
The interesting bit: there is no secret protocol
I expected a proprietary binary channel. What I found is this:
| What | How |
|---|---|
| Printing | IPP (Print-Job, Get-Printer-Attributes), the same thing CUPS speaks |
| Scanning | eSCL (/eSCL/ScannerCapabilities, /eSCL/ScanJobs) |
| Status, ink, alerts, counters | LEDM, HP’s REST+XML under /DevMgmt/* |
| Newer settings | CDM, JSON under /cdm/*, each resource with a /constraints endpoint listing the allowed values |
The cloud is for accounts, Instant Ink and telemetry. Local device control does not need it.
Two details made the difference:
- HP extends IPP. Besides the standard attributes, the printer returns vendor ones in the same
Get-Printer-Attributesanswer, such ashp-easycolor-default,hp-edge-to-edge-support,hp-print-quality-supported, plus a group for job storage and quotas on bigger models. You only have to ask and print them. - The service tools are hidden, not missing. My first probe said the printer had no cleaning or diagnostics actions.
DiscoveryTree.xmlsimply does not listInternalPrint. ButGET /DevMgmt/InternalPrintCap.xmlreturns 200 with 17 job types, fromcleaningPageandpqDiagnosticsPagetonetworkSummaryandwifiDirectGuide. The decompiledInternalPrintDynHelper.InitiateInternalPrintJobshowed me the exact POST body. I sent aconfigurationPagejob and the printer printed it.
hpfree
I wrote the IPP encoder and decoder myself, so the tool has a small dependency footprint. It also has an eSCL client, a LEDM client and a UI server bound to 127.0.0.1 only.
hpfree discover # mDNS, _ipp._tcp
hpfree status 192.168.1.50 # state, inks, alerts
hpfree print 192.168.1.50 file.pdf --sides two-sided-long-edge --color monochrome
hpfree scan 192.168.1.50 scan.jpg --dpi 300 --size a4
hpfree tools 192.168.1.50 configurationPage --yes
hpfree set 192.168.1.50 contrast 40 # validated against /constraints
hpfree cloud-off 192.168.1.50 # turns off cloud services
Print options are checked against what the printer says it supports before anything is sent. Settings are written by PUT of the whole resource, and after each write the tool reads the value back and fails if it does not match. Anything that physically does something (service pages, head alignment) needs --yes, and the UI server enforces that as well, not just the button.
A few things I only learned by running it on real hardware:
- Element order in the eSCL request matters. In the wrong order the printer silently ignores
XResolutionand scans at 300 dpi. There is a test pinning the order now. - Service jobs live in a separate queue. They are in LEDM at
/Jobs/JobList/<n>and never show up in IPPGet-Jobs, sohpfree jobshas to merge both. - The ENVY 6000 has a glass-only scanner.
--feederreturns a readable error instead of a timeout.
A review caught my own bug
I had hpfree align working, with POST to /Calibration/Session and polling until done. Then I reviewed my own code against the manifest the printer actually returned, and it was not good:
- no confirmation on the API endpoint, so anything on localhost could trigger a print cycle
- the
Locationheader was used as-is, so an absolute URL got glued onto the host - completion was detected with a string
contains, so<cal:CalibrationState>Completedwith a namespace prefix was never recognised - a timeout returned
Ok, so the UI showed “Completed” for a job that had merely stopped being watched - POST was retried after a transport error, which can start a second session if the first response was lost
It now parses the XML by local name, checks that Location points at the same printer and a single session, never retries the POST, and treats only an explicit Completed as success. Failed, cancelled, timed out and unknown are separate outcomes. 81 tests pass, many of them against a fake HTTP printer. Still, I have not watched a full alignment run on the real device, and the docs say so.
Privacy switches, with caveats
hpfree cloud-off flips CloudServicesSwitch and MobileAppsService in /ePrint/ePrintConfigDyn.xml, and hpfree usage-data turns off the usage data consent. Both read the value back after writing.
What this proves is that the printer’s configuration says “off” right now. It does not prove that no traffic goes to HP, and it does not prove the setting survives a reboot or a firmware update. I have only unit-tested both commands so far, so treat them as unverified on live hardware. Open question number one on my list is whether HP quietly turns it back on.
Not there yet
- Everything is tested on one model, an ENVY 6000. Laser printers and ADF scanners are untested because I do not own them.
- No TLS or authentication in the client, so use it on a trusted network only.
I analysed an app installed on my own machine and talked only to my own printer, which is also the scope I would recommend. Not affiliated with HP.