From e43ec51a71b0aa142c5cab3c1c737ea890150a17 Mon Sep 17 00:00:00 2001
From: Dante Catalfamo
Date: Fri, 15 Oct 2021 15:26:06 -0400
Subject: xprinter-wifi: move to non-WIP folder, no longer draft

---
 .../posts/xprinter-wifi/80XX_Programmer_Manual.pdf | Bin 0 -> 1084986 bytes
 content/posts/xprinter-wifi/index.org              | 352 +++++++++++++++++++++
 content/posts/xprinter-wifi/xprinter_advanced.png  | Bin 0 -> 24148 bytes
 content/posts/xprinter-wifi/xprinter_lp0.reg       | Bin 0 -> 216 bytes
 content/posts/xprinter-wifi/xprinter_set_net.png   | Bin 0 -> 11419 bytes
 .../posts/xprinter-wifi/xprinter_setup_tool.png    | Bin 0 -> 46156 bytes
 .../posts/xprinter-wifi/xprinter_usb_wireshark.png | Bin 0 -> 1084854 bytes
 .../xprinter-wifi/xprinter_wifi_screenshot.png     | Bin 0 -> 123067 bytes
 .../posts/xprinter-wifi/xprinter_wine_regedit.png  | Bin 0 -> 18164 bytes
 content/posts/xprinter-wifi/xprinter_wireshark.png | Bin 0 -> 160032 bytes
 10 files changed, 352 insertions(+)
 create mode 100644 content/posts/xprinter-wifi/80XX_Programmer_Manual.pdf
 create mode 100644 content/posts/xprinter-wifi/index.org
 create mode 100644 content/posts/xprinter-wifi/xprinter_advanced.png
 create mode 100644 content/posts/xprinter-wifi/xprinter_lp0.reg
 create mode 100644 content/posts/xprinter-wifi/xprinter_set_net.png
 create mode 100644 content/posts/xprinter-wifi/xprinter_setup_tool.png
 create mode 100644 content/posts/xprinter-wifi/xprinter_usb_wireshark.png
 create mode 100644 content/posts/xprinter-wifi/xprinter_wifi_screenshot.png
 create mode 100644 content/posts/xprinter-wifi/xprinter_wine_regedit.png
 create mode 100644 content/posts/xprinter-wifi/xprinter_wireshark.png

(limited to 'content/posts/xprinter-wifi')

diff --git a/content/posts/xprinter-wifi/80XX_Programmer_Manual.pdf b/content/posts/xprinter-wifi/80XX_Programmer_Manual.pdf
new file mode 100644
index 0000000..83579ca
Binary files /dev/null and b/content/posts/xprinter-wifi/80XX_Programmer_Manual.pdf differ
diff --git a/content/posts/xprinter-wifi/index.org b/content/posts/xprinter-wifi/index.org
new file mode 100644
index 0000000..a1e4be2
--- /dev/null
+++ b/content/posts/xprinter-wifi/index.org
@@ -0,0 +1,352 @@
+#+TITLE: Reverse engineering a thermal printer's WiFi setup commands
+#+DATE: 2021-10-12T15:57:12-04:00
+#+DRAFT: false
+#+SHOWTOC: true
+#+DESCRIPTION: Setting up an Xprinter thermal receipt printer WiFi from Linux
+#+TAGS[]: linux hardware
+#+KEYWORDS[]: linux hardware
+#+SLUG:
+#+SUMMARY:
+
+* Introduction
+
+  I recently purchased a thermal receipt printer off of [[https://web.archive.org/web/20211012195845/https://www.aliexpress.com/item/32842111016.html?spm=a2g0s.9042311.0.0.207d4c4d6xNWgO][AliExpress]] for a
+  project. It features both WiFi and USB connectivity which I thought
+  was really cool for the price.
+
+  To my dismay, I realized after purchasing that the drivers and
+  configuration application only run on Windows.
+
+  This wasn't a huge deal, as thermal printers generally use the
+  somewhat kinda standardized command set called [[https://github.com/escpos/escpos][ESC/POS]]. Unfortunately
+  while many of the formatting commands are shared between printers, the
+  commands to setup the WiFi connection don't seem to be documented
+  anywhere, and I suspect are device-specific.
+
+  Since booting into Windows every time I want to manage the printer's
+  network settings isn't ideal, I decided to reverse engineer the
+  WiFi configuration commands.
+
+  Initially I tried to run the configuration tool in wine, but it
+  couldn't communicate with the printer over USB, which wasn't too
+  surprising.
+
+* Running in Windows
+
+  I booted my spare laptop into windows and launched the config tool
+  there.
+
+  I then setup the WiFi through =Advanced -> Set Net=
+
+  #+ATTR_HTML: :title Xprinter config tool running in wine
+  #+ATTR_HTML: :alt Xprinter config tool running in wine
+  [[file:xprinter_setup_tool.png]]
+
+
+  #+ATTR_HTML: :title Xprinter advanced settings
+  #+ATTR_HTML: :alt Xprinter advanced settings
+  [[file:xprinter_advanced.png]]
+
+  #+ATTR_HTML: :title Xprinter net settings
+  #+ATTR_HTML: :alt Xprinter net settings
+  [[file:xprinter_set_net.png]]
+
+  At this point I noticed that the application supported configuring the
+  printer over the network, meaning I might be able to change the
+  settings under wine again, as network sockets should work normally.
+
+* Wireshark on Linux
+
+  After rebooting into Linux and testing my assumption, it turned out to
+  be half true, I was able to run the configuration tool over the
+  network without issue and print from it, but I couldn't configure the
+  WiFi.
+
+  While I could have stopped there I decided to go one step further and
+  reverse the command sequence that configures the WiFi settings so I
+  could re-configure the printer's WiFi over USB if it ever got messed
+  up.
+
+  To do that, I sniffed the traffic going from the Xprinter application
+  and the printer's socket using Wireshark, assuming the commands it
+  sends over the network are the same ones it sends over USB.
+
+  [[file:xprinter_wireshark.png]]
+
+  In the examples below I've encoded the data being sent to hex to make
+  it easier to understand the contents of the packets.
+
+  Based on the traffic, I was able to come up with the following.
+
+** Setting IP address
+
+   The following sets
+   - IP =192.168.0.7=
+
+   The IP hex:
+   #+begin_src ruby
+   [192, 168, 0, 7].map { _1.to_s(16).rjust(2, '0') }
+   #+end_src
+
+   #+RESULTS:
+   | c0 | a8 | 00 | 07 |
+
+   Packet contents from wireshark:
+
+   =0000   1f 1b 1f 22 c0 a8 00 07=
+
+   | Description    | Characters    |
+   |----------------+---------------|
+   | Unit separator | =1f=          |
+   | Escape         | =1b=          |
+   | Unit separator | =1f=          |
+   | Command code   | =22=          |
+   | IP             | =c0 a8 00 07= |
+
+** Setting subnet Mask
+
+   The following sets
+   - Subnet mask =255.255.255.0=
+
+   Subnet mask to hex:
+   #+begin_src ruby
+   [255, 255, 255, 0].map { _1.to_s(16).rjust(2, '0') }
+   #+end_src
+
+   #+RESULTS:
+   | ff | ff | ff | 00 |
+
+   Packet contents from wireshark:
+
+   =0000   1f 1b 1f b0 ff ff ff 00=
+
+   | Description    | Character     |
+   |----------------+---------------|
+   | Unit separator | =1f=          |
+   | Escape         | =1b=          |
+   | Unit separator | =1f=          |
+   | Command code   | =b0=          |
+   | Subnet mask    | =ff ff ff 00= |
+
+** Setting gateway
+
+   The following sets
+   - Gateway =192.168.0.1=
+
+   Subnet mask to hex:
+   #+begin_src ruby
+   [192, 168, 0, 1].map { _1.to_s(16).rjust(2, '0') }
+   #+end_src
+
+   #+RESULTS:
+   | c0 | a8 | 00 | 01 |
+
+   Packet contents from wireshark:
+
+   =0000   1f 1b 1f b1 c0 a8 00 01=
+
+   | Description    | Character     |
+   |----------------+---------------|
+   | Unit separator | =1f=          |
+   | Escape         | =1b=          |
+   | Unit separator | =1f=          |
+   | Command code   | =b1=          |
+   | Net Mask        | =c0 a8 00 01= |
+
+** Setting IP, subnet mask, and gateway
+
+   The following sets
+   - IP =192.168.0.1=
+   - Subnet mask =255.255.255.0=
+   - Gateway =192.168.0.1=
+
+   Packet contents from wireshark:
+
+   =0000   1f 1b 1f b2 c0 a8 00 07 ff ff ff 00 c0 a8 00 01=
+
+   | Purpose        | Character     |
+   |----------------+---------------|
+   | Unit separator | =1f=          |
+   | Escape         | =1b=          |
+   | Unit separator | =1f=          |
+   | Command code   | =b2=          |
+   | IP             | =c0 a8 00 07= |
+   | Subnet mask    | =ff ff ff 00= |
+   | Gateway        | =c0 a8 00 01= |
+
+** Setting WiFi network
+
+   The following sets
+   - SSID =SSID_HERE=
+   - Key =PASSWORD_HERE=
+   - Key Type =WPA2_AES_PSK=
+
+   SSID to hex:
+   #+begin_src ruby
+   "SSID_HERE".bytes.map { _1.to_s(16) }
+   #+end_src
+
+   #+RESULTS:
+   | 53 | 53 | 49 | 44 | 5f | 48 | 45 | 52 | 45 |
+
+   Key to hex:
+   #+begin_src ruby
+   "PASSWORD_HERE".bytes.map { _1.to_s(16) }
+   #+end_src
+
+   #+RESULTS:
+   | 50 | 41 | 53 | 53 | 57 | 4f | 52 | 44 | 5f | 48 | 45 | 52 | 45 |
+
+   Packet contents from wireshark (including string representation):
+     #+begin_src
+     0000   1f 1b 1f b3 06 53 53 49 44 5f 48 45 52 45 00 50   .....SSID_HERE.P
+     0010   41 53 53 57 4f 52 44 5f 48 45 52 45 00            ASSWORD_HERE.
+     #+end_src
+
+   | Purpose         | Character       |
+   |-----------------+-----------------|
+   | Unit separator  | =1f=            |
+   | Escape          | =1b=            |
+   | Unit separator  | =1f=            |
+   | Command code    | =b3=            |
+   | Key type        | =06=            |
+   | SSID            | =SSID_HERE=     |
+   | NUL-termination | =00=            |
+   | Key             | =PASSWORD_HERE= |
+   | NUL-termination | =00=            |
+
+   If the WiFi key type is anything like the menu, the other key types
+   are as follows
+
+   | Key Type             | Value |
+   |----------------------+-------|
+   | =NULL=               | =00=  |
+   | =WEP64=              | =01=  |
+   | =WEP128=             | =02=  |
+   | =WPA_AES_PSK=        | =03=  |
+   | =WPA_TKIP_PSK=       | =04=  |
+   | =WPA_TKIP_AES_PSK=   | =05=  |
+   | =WPA2_AES_PSK=       | =06=  |
+   | =WPA2_TKIP=          | =07=  |
+   | =WPA2_TKIP_AES_PSK=  | =08=  |
+   | =WPA_WPA2_MixedMode= | =09=  |
+
+** Setting all network options
+
+   The following sets
+   - IP =192.168.0.7=
+   - Subnet mask =255.255.255.0=
+   - Gateway =192.168.0.1=
+   - SSID =SSID_HERE=
+   - Key =PASSWORD_HERE=
+   - Key Type =WPA2_AES_PSK=
+
+   Packet contents from wireshark (including string representation):
+
+   #+begin_src
+   0000   1f 1b 1f b4 c0 a8 00 07 ff ff ff 00 c0 a8 00 01   ................
+   0010   06 53 53 49 44 5f 48 45 52 45 00 50 41 53 53 57   .SSID_HERE.PASSW
+   0020   4f 52 44 5f 48 45 52 45 00                        ORD_HERE.
+   #+end_src
+
+   | Description     | Character       |
+   |-----------------+-----------------|
+   | Unit Separator  | =1f=            |
+   | Escape          | =1b=            |
+   | Unit Separator  | =1f=            |
+   | Command Code    | =b4=            |
+   | IP              | =c0 a8 00 07=   |
+   | NetMask         | =ff ff ff 00=   |
+   | Gateway         | =c0 a8 00 01=   |
+   | Key Type        | =06=            |
+   | SSID            | =SSID_HERE=     |
+   | NUL-termination | =00=            |
+   | Key             | =PASSWORD_HERE= |
+   | NUL-termination | =00=            |
+
+* Post-packet analysis
+
+  At this point, after writing an application that could send
+  identical packets given the correct input, I realized that for some
+  reason, my printer was not responding to the commands issued from
+  either the config utility or my program.
+
+  I tried to look deeper for better documentation, but was only able
+  to come across [[file:80XX_Programmer_Manual.pdf][this PDF]] from their [[https://xprinter.com.ua/terms--conditions.html][Russian language website]], which
+  unfortunately still didn't contain the WiFi setup instructions.
+
+  I was also able to find [[https://github.com/daotuyen9244/XprinterPOS][this]] GitHub repo that seems to contain some
+  commands for Xprinter systems, but not the ones I need.
+
+  The data sheets on the Xprinter website claims they have the Linux
+  test utility, which should contain the necessary tools to configure
+  wifi on the printers, but it seems they only support Android and
+  Windows.
+
+* Wine USB attempt 2
+
+  I tried again to get the printer software to work under wine. It
+  turns out wine only looks at =/dev/lp*= devices by default and
+  doesn't add =/dev/usb/lp*=. This time I searched the wine wiki for
+  ways to get the Linux =/dev/usb/lp0= device to show up as =LPT1=
+  under wine. After some digging it appears you can tell wine which
+  devices to map to =COM= / =LPT= ports with registry values.
+
+  It's described in section [[https://wiki.winehq.org/Wine_User's_Guide#Serial_and_Parallel_Ports][4.3.1]] on the [[https://wiki.winehq.org/Wine_User's_Guide][Wine User's Guide]].
+
+  I followed the guide and created the following registry key.
+
+  #+ATTR_HTML: :title Adding the registry key in wine
+  #+ATTR_HTML: :alt wine regedit
+  [[file:xprinter_wine_regedit.png]]
+
+  I then restarted the wine server using the following command.
+
+  #+begin_src shell
+  wineserver -k
+  #+end_src
+
+  I've exported the registry entry [[file:xprinter_lp0.reg][here]] in case anyone wants to do the
+  same.
+
+  At that point the Xprinter setup tool was able to recognize the
+  printer as =LPT1=.
+
+  I then setup wireshark to be able to sniff USB traffic using the
+  their guide [[https://wiki.wireshark.org/CaptureSetup/USB][here]].
+
+  From there I was able to figure out which USB hub it was running
+  through and its device ID, and filter it out using a wireshark
+  filter.
+
+  #+ATTR_HTML: :title wireshark USB sniffing
+  #+ATTR_HTML: :alt wireshark USB sniffing
+  [[file:xprinter_usb_wireshark.png]]
+
+  After sniffing the USB traffic sent by the xprinter configuration
+  app, it looks identical to what was being sent over the TCP
+  connection, meaning what I built should have worked.
+
+* The solution
+
+  So apparently I made some assumptions when starting this project
+  that turned out to not be true.
+
+  It seems only the command to set all options at once consistently
+  works, even when using the Xprinter setup tool from within Windows.
+  The printer will also not allow you to reconfigure it's WiFi unless
+  connected over USB.
+
+  After I narrowed down the number of commands I was testing to only
+  =xb4= and only trying over USB, it worked fine.
+
+  I suppose I should have checked that all the commands worked
+  properly from the beginning, but I did learn a lot along the way
+  so it wasn't a total loss.
+
+  After figuring out the issue, I wrote a small command line tool to
+  configure the printer. You can check it out [[https://github.com/dantecatalfamo/xprinter-wifi][here]].
+
+  #+ATTR_HTML: :title Xprinter WiFi config tool screenshot
+  #+ATTR_HTML: :alt Xprinter WiFi config tool screenshot
+  [[file:xprinter_wifi_screenshot.png]]
diff --git a/content/posts/xprinter-wifi/xprinter_advanced.png b/content/posts/xprinter-wifi/xprinter_advanced.png
new file mode 100644
index 0000000..ded4185
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_advanced.png differ
diff --git a/content/posts/xprinter-wifi/xprinter_lp0.reg b/content/posts/xprinter-wifi/xprinter_lp0.reg
new file mode 100644
index 0000000..1233a6d
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_lp0.reg differ
diff --git a/content/posts/xprinter-wifi/xprinter_set_net.png b/content/posts/xprinter-wifi/xprinter_set_net.png
new file mode 100644
index 0000000..329422a
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_set_net.png differ
diff --git a/content/posts/xprinter-wifi/xprinter_setup_tool.png b/content/posts/xprinter-wifi/xprinter_setup_tool.png
new file mode 100644
index 0000000..a5e5004
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_setup_tool.png differ
diff --git a/content/posts/xprinter-wifi/xprinter_usb_wireshark.png b/content/posts/xprinter-wifi/xprinter_usb_wireshark.png
new file mode 100644
index 0000000..18a8bfb
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_usb_wireshark.png differ
diff --git a/content/posts/xprinter-wifi/xprinter_wifi_screenshot.png b/content/posts/xprinter-wifi/xprinter_wifi_screenshot.png
new file mode 100644
index 0000000..be5ff65
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_wifi_screenshot.png differ
diff --git a/content/posts/xprinter-wifi/xprinter_wine_regedit.png b/content/posts/xprinter-wifi/xprinter_wine_regedit.png
new file mode 100644
index 0000000..c8ff03c
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_wine_regedit.png differ
diff --git a/content/posts/xprinter-wifi/xprinter_wireshark.png b/content/posts/xprinter-wifi/xprinter_wireshark.png
new file mode 100644
index 0000000..1b70a27
Binary files /dev/null and b/content/posts/xprinter-wifi/xprinter_wireshark.png differ
-- 
cgit v1.2.3