<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: endes0</title>
    <description>The latest articles on DEV Community by endes0 (@endes).</description>
    <link>https://dev.to/endes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1528479%2F638e7836-abc0-4204-bf55-436182515c87.jpg</url>
      <title>DEV Community: endes0</title>
      <link>https://dev.to/endes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/endes"/>
    <language>en</language>
    <item>
      <title>Retevis custom programming cable and unlocking</title>
      <dc:creator>endes0</dc:creator>
      <pubDate>Sun, 13 Jul 2025 12:58:01 +0000</pubDate>
      <link>https://dev.to/endes/retevis-custom-programming-cable-and-unlocking-4j47</link>
      <guid>https://dev.to/endes/retevis-custom-programming-cable-and-unlocking-4j47</guid>
      <description>&lt;p&gt;I recently bought a pair of Retevis RT622P walkie, and while reading the manual I discovered that they have a software for configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7vr3q3vnfa1vl0ze9op0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7vr3q3vnfa1vl0ze9op0.png" alt="Retevis RT622P programming software" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is very simple, in fact it works decently on wine. The communication is thought the com ports and in fact the software comes with driver for the PL23xx serial to usb. Silly me I thought the walkie had this converter integrate and you only need to connect them to the PC with the USB C cable. NO, they sell to you an "programming cable" for $28.99 on the official store or 5 € on Aliexpress. This cable is the same for the &lt;strong&gt;RT20 RT65 RT665 RB19 RB619 RT622P RT22P&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Making a custom cable
&lt;/h1&gt;

&lt;p&gt;I didn't want to buy yet another USB to serial adapter, so after a little teardown, I traced back where they connected the serial lines. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28yr6d1lbrt7rngzmlri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28yr6d1lbrt7rngzmlri.png" alt="USB C PCB traces" width="800" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They connected &lt;strong&gt;TX and RX to USB D+ and D- directly&lt;/strong&gt;, so after a little bit of getting "creative"(doing shoddy work), I managed to connect the walkie to the software.&lt;/p&gt;

&lt;p&gt;Also, if you are using wine, &lt;a href="https://superuser.com/a/1471614" rel="noopener noreferrer"&gt;you should do this to map your serial port&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Unlocking
&lt;/h1&gt;

&lt;p&gt;The software let you change every config for the channels except the &lt;strong&gt;frequency, TX power and wideband&lt;/strong&gt;. This I believe it is for legal reasons (Those devices are unlicensed legal PMR446). But if you go to &lt;strong&gt;About-&amp;gt;Setup&lt;/strong&gt; and in &lt;strong&gt;password&lt;/strong&gt; you put &lt;strong&gt;"retevis"&lt;/strong&gt; these settings become editable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsr4ct56eg2a9m29xssr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsr4ct56eg2a9m29xssr.png" alt=" " width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faso5ai84o3nrf6op0kya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faso5ai84o3nrf6op0kya.png" alt=" " width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Communication protocol
&lt;/h1&gt;

&lt;p&gt;The serial port is opened at &lt;strong&gt;9600 bauds 8N1 parity&lt;/strong&gt;.&lt;br&gt;
For describing the communication, I will put in &lt;em&gt;italics the hexadecimals values&lt;/em&gt;, &lt;strong&gt;SW send&lt;/strong&gt; when the software sends out a data to the serial port and &lt;strong&gt;walk respond&lt;/strong&gt; when the walkie send data to the software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start
&lt;/h2&gt;

&lt;p&gt;The following sequence is followed each time the software is going to write or read from the walkie.&lt;/p&gt;

&lt;p&gt;SW send: &lt;em&gt;02 43 31 36 30 52 41 4d&lt;/em&gt; (.C160RAM)&lt;br&gt;
walk respond: &lt;em&gt;06&lt;/em&gt;&lt;br&gt;
SW send: &lt;em&gt;02&lt;/em&gt;&lt;br&gt;
walk respond: &lt;em&gt;06 00 00 00 00 00 00 00&lt;/em&gt;&lt;br&gt;
SW send: &lt;em&gt;06&lt;/em&gt;&lt;br&gt;
walk respond: &lt;em&gt;06&lt;/em&gt;&lt;br&gt;
SW send: &lt;em&gt;05&lt;/em&gt;&lt;br&gt;
walk respond: &lt;em&gt;ff ff ff ff ff ff&lt;/em&gt; (This possibly is the RW password)&lt;br&gt;
SW send: &lt;em&gt;06&lt;/em&gt;&lt;br&gt;
walk respond: &lt;em&gt;06&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Write to the walkie
&lt;/h2&gt;

&lt;p&gt;When write to walkie is selected, the software start to send sequences like &lt;em&gt;57 00 1a 0d f5 96 a8 02 f5 96 a8 02 48 11 48 11 ef&lt;/em&gt; (the same as the Dat files) and receiving &lt;em&gt;06&lt;/em&gt;. Finally, the software send &lt;em&gt;62&lt;/em&gt; (Reset?).&lt;/p&gt;

&lt;h2&gt;
  
  
  Read from the walkie
&lt;/h2&gt;

&lt;p&gt;When read from walkie is selected, the software start to send sequences like &lt;em&gt;52 00 XX 0d&lt;/em&gt; and receiving &lt;em&gt;57 ...&lt;/em&gt; sequences like the Dat files). XX is the 3rd value of the &lt;em&gt;57 ...&lt;/em&gt; sequences, it is basically an index value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dat files
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2ozqacemw99flj3z05k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2ozqacemw99flj3z05k.png" alt="Dat file" width="511" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They contain all the channels and other configs in a sort of tabular storage. You can see where each parameter is by saving two of these files with different values for that parameter. There are for sure some "hidden" parameters the software cannot change but I don't have the time to play around with them right now.&lt;/p&gt;

</description>
      <category>reverseengineering</category>
      <category>hardware</category>
    </item>
    <item>
      <title>Analyzing the communication protocol of the Decathlon Walk by domyos</title>
      <dc:creator>endes0</dc:creator>
      <pubDate>Fri, 20 Dec 2024 17:06:29 +0000</pubDate>
      <link>https://dev.to/endes/analyzing-the-communication-protocol-of-the-decathlon-walk-by-domyos-3a36</link>
      <guid>https://dev.to/endes/analyzing-the-communication-protocol-of-the-decathlon-walk-by-domyos-3a36</guid>
      <description>&lt;p&gt;Some months ago I bought a treadmill from Decathlon, A "Walk by domyos", a pretty basic model. It has a console where you can set the speed and see a variety of metrics (total exercise time, total kms, etc.). Of course, some weeks later, I could not resist the urge to open it up and reverse engineering it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Teardown
&lt;/h1&gt;

&lt;p&gt;Of course the first thing I did was to open it up, the entire thing (or at least the console and motor control board) seems to be manufactured by &lt;a href="https://sheway.com/" rel="noopener noreferrer"&gt;ShangaHai Electronics Way Co. Ltd&lt;/a&gt; (It took me a while to find the manufacturer website, it doesn't pop up when you search it). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwnp2ucs6t2urw1nf191.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwnp2ucs6t2urw1nf191.png" alt="Image description" width="576" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fraitgx0inos5ye9vqf2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fraitgx0inos5ye9vqf2h.png" alt="Image description" width="576" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxd9ixz7voca04khz3dig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxd9ixz7voca04khz3dig.png" alt="Image description" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The motor control board is labeled as the model &lt;strong&gt;B408D&lt;/strong&gt;. The microcontroller and other logic ics are on another pcb soldered perpendicular to the main one, and it seems to have a layer of some protective material on top of the board, so reading the ics markings is difficult. There is also a 4 pin header labeled "PROGRAM", 2 of these pins are ground and 5V, I'm pretty sure this is the programming port used to upload the firmware. The connector to the console board is well labeled on every pin and isolated by optocouplers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfrgc209bk7ok5uhiff0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfrgc209bk7ok5uhiff0.png" alt="Image description" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foe4d9viqbk6pxay0hk5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foe4d9viqbk6pxay0hk5d.png" alt="Image description" width="800" height="864"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The console board is labeled as "A1835B1", the labeling seems to also includes the firmware file on the mcu: &lt;strong&gt;"TC8_A1835_S115_60509.mot"&lt;/strong&gt;. The board has basically a main mcu and a display driver (the &lt;a href="https://www.holtek.com/page/vg/ht1621" rel="noopener noreferrer"&gt;ht1621&lt;/a&gt;). Identifying the mcu is not easy, the package is a TSSOP20 and has the following markings "0C000 H3AG", which are not very helpful. After searching "0c000 mcu tssop20" on google this showed up on &lt;a href="https://es.aliexpress.com/i/1005002625985673.html" rel="noopener noreferrer"&gt;Aliexpress&lt;/a&gt;, so it seems to be a Renesas mcu, maybe a r8c o rl78 series. The main mcu is not directly connected to the RX and TX pins of the connector to the motor control board, but through a circuitry that does a voltage level shifting.&lt;/p&gt;

&lt;p&gt;The cable that connects both the board is basically made of 5 lines: The power lines, 12v and GND; the communication lines, RX and TX; and the "SW", that the console connects to 12v when the key is inserted.&lt;/p&gt;

&lt;p&gt;I didn't do any more analysis or found anything interesting on the boards, sure it will be interesting to analyze how the motor boards control the motor itself at an electronic level, but it is outside of my knowledge area and sincerely for me, it would be boring.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tapping the communication between the console and motor board
&lt;/h1&gt;

&lt;p&gt;Now comes the fun part, and of course the most important, as if we reverse the communication protocol we can potentially replace the console with one custom-made with more functionalities or do some fun things (like going more than 8km/h).&lt;/p&gt;

&lt;p&gt;The first thing I did was to plug in my cheap logic analyzer into the RX and TX cables without even thinking about if the voltages level were compatible. Of course, it seems that these voltages were too high, and a fun thing happened: When I plugged the analyzer on both cables, the communication got interrupted and the error 5 showed up on the console, but  if I only plugged it on the receiving part of each board it didn't interfere.&lt;/p&gt;

&lt;p&gt;Fortunately, there were 3 jumpers(0 ohm resistors) nearby the mcu, these were for gnd, rx and tx lines. Luckily those RX and TX lines were before the level shifting and can be tapped without problems. After taking some captures I could decode the protocol as UART at a baud rate of 2400 bauds. For discovering this baud rate, which for me was a very unusual baud as I have never seen this value, but is understandable as maybe a higher rate would be problematic because the cable length or the interference (the motor generates a lot), I used the Sigrok guess baudrate function, which resulted to be very useful.&lt;/p&gt;

&lt;p&gt;Unfortunately, while doing some experiments with the console, I accidentally inverted the polarity and broke something, possibly the input pin on the mcu that detects that the key is present. So the console now is useless, which means I could not continue to do all the tests I was doing, so the next parts may come short in some aspects.&lt;/p&gt;

&lt;h1&gt;
  
  
  Analyzing the messages
&lt;/h1&gt;

&lt;p&gt;All the messages sent by both the console and motor control board follows the next structure.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte position&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;2..n&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;n+1&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;n+2&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Header&lt;/td&gt;
&lt;td&gt;length&lt;/td&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;Checksum&lt;/td&gt;
&lt;td&gt;Footer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Seems to be the unit address. &lt;strong&gt;0x68&lt;/strong&gt; for the console. &lt;strong&gt;0x73&lt;/strong&gt; for the motor board.&lt;/td&gt;
&lt;td&gt;Byte size of the data + checksum + footer&lt;/td&gt;
&lt;td&gt;Content.  Console normally sends 7** bytes** and the motor &lt;strong&gt;10 bytes&lt;/strong&gt;.&lt;/td&gt;
&lt;td&gt;Sum of the data and length mod 0xFF&lt;/td&gt;
&lt;td&gt;Always &lt;strong&gt;0x43&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x68&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x8&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x88&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x43&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Console data
&lt;/h2&gt;

&lt;p&gt;The console every second or so sends a message containing the following 7 bytes of data.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte position&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key status?&lt;/td&gt;
&lt;td&gt;Start/stop&lt;/td&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0x80&lt;/strong&gt; if key is present, &lt;strong&gt;0x81&lt;/strong&gt; if not.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0x80&lt;/strong&gt; when the treadmill is running, 0 when not.&lt;/td&gt;
&lt;td&gt;selected km/h * 10&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x80&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x80&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x06&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x0&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On startup, after an initial delay, the console sends this &lt;strong&gt;31 bytes&lt;/strong&gt; message multiple times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x87, 0x2, 0x24, 0x29, 0, 0, 0, 0, 0x1, 0xc, 0, 0, 0x8, 0x8, 0, 0, 0, 0, 0, 0, 0, 0x19, 0, 0, 0x50, 0, 0, 0, 0, 0, 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I really don't know what it is, maybe the configuration for the motor board?. It doesn't seem to change, I have tried to reset the console, change the units or increment the total kilometers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Motor board data
&lt;/h2&gt;

&lt;p&gt;In response to the message sent by the console, the motor board sends a message containing the following 10 bytes data.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Byte position&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;9&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;unknown&lt;/td&gt;
&lt;td&gt;Measured speed&lt;/td&gt;
&lt;td&gt;voltage?&lt;/td&gt;
&lt;td&gt;Another measured speed?&lt;/td&gt;
&lt;td&gt;motor amperage?&lt;/td&gt;
&lt;td&gt;unknown&lt;/td&gt;
&lt;td&gt;status&lt;/td&gt;
&lt;td&gt;unknown&lt;/td&gt;
&lt;td&gt;unknown&lt;/td&gt;
&lt;td&gt;unknown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;always 0xa0&lt;/td&gt;
&lt;td&gt;From the IR rotatory sensor. km/h * 10&lt;/td&gt;
&lt;td&gt;Fluctuates between 230 – 250.&lt;/td&gt;
&lt;td&gt;km/h * 10&lt;/td&gt;
&lt;td&gt;see below&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;td&gt;0x21 when ready (you can hear a relay clicking) 0x22 or 0x23 when running&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;td&gt;Always 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0xa0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x05&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0xf6&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x06&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0xa&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0x22&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;0&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The byte 4 (motor amperage?) seems something that is directly measured from the motor, it fluctuates but seems to increment with the speed (see the table below) and if the motor gets disconnected from the board it goes to a pretty high value (~100).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;speed(km/h)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.5&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h1&gt;
  
  
  Errors code
&lt;/h1&gt;

&lt;p&gt;The console can give a variety of errors to the user, I thought it would be interesting documenting each one, as the official manual is not very helpful. But, as I said earlier I broke my console board halfway through, so this part is pretty incomplete. In fact I really don't know how most errors get triggered with the motor board messages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnawy0672nxje0humuppa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnawy0672nxje0humuppa.png" alt="Image description" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error 2: It popups when the motor is disconnected from the control board.&lt;/li&gt;
&lt;li&gt;Error 5: Display board after startup is not receiving motor board messages or are malformed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the way, disconnecting the IR rotary speed sensor doesn't seems to trigger an error.&lt;/p&gt;
&lt;h1&gt;
  
  
  Making a very simple console
&lt;/h1&gt;

&lt;p&gt;As I have broken mine and it is not easy to find, I had to do one for still using the treadmill and not having a giant paperweight at home. The first thing I did for making it run was creating and running &lt;a href="https://bsky.app/profile/fwrret.bsky.social/post/3lbd2lxn4t22q" rel="noopener noreferrer"&gt;a python script in my pc connected through a serial adapter and reusing the level shifting circuitry of the old board&lt;/a&gt;. It was not very comfortable to use the treadmill in that way. So with an Arduino UNO, a touch screen shield (MCUFRIEND 2.4” TFT Display) and a horrible written code (I swear I'm a professional software developer, during my shift) &lt;a href="https://bsky.app/profile/fwrret.bsky.social/post/3lbi76z2ue223" rel="noopener noreferrer"&gt;I made a simple console to set the speed and start and stop the treadmill&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is the gist of the code, keep in mind that is something that I did very fast and when I was tired.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h1&gt;
  
  
  What's left
&lt;/h1&gt;

&lt;p&gt;There are still things left to do for the future, so I might do a follow up post.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I want to finish understanding and documenting both message data, especially the control board messages, which I can still capture and do tests.&lt;/li&gt;
&lt;li&gt;As I broke the console I should try to dump the firmware, so I can reverse engineer the code and so I maybe understand more the messages and errors.&lt;/li&gt;
&lt;li&gt;Implement a better console with more functionalities, with an esp32 so I could do cool things.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reverseengineering</category>
    </item>
    <item>
      <title>USB HID Down the rabbit hole: Logitech G435 dongle</title>
      <dc:creator>endes0</dc:creator>
      <pubDate>Sun, 30 Jun 2024 11:32:21 +0000</pubDate>
      <link>https://dev.to/endes/usb-hid-down-the-rabbit-hole-logitech-g435-dongle-33if</link>
      <guid>https://dev.to/endes/usb-hid-down-the-rabbit-hole-logitech-g435-dongle-33if</guid>
      <description>&lt;p&gt;Last time, in the first post of this series, I explored the vendor interface of my Logitech Mouse, now let's explore the dongle of my headphones. As I said last time, it caught my attention that it can send "phone" HID events. Also, Logitech doesn't provide any official software for configuring the headphones(like the sidetone volume) or seeing their status(like the battery remaining).&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1805693034950181110-778" src="https://platform.twitter.com/embed/Tweet.html?id=1805693034950181110"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1805693034950181110-778');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1805693034950181110&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The dongle I have is the HDT647(PID 0acb). After a bit of research, the first thing that pop up is the &lt;a href="https://fccid.io/JNZA00150/Internal-Photos/11-Internal-Photo-JNZA00150-5359099"&gt;FCC Internal teardown images report&lt;/a&gt;. Unfortunately, the chips markings aren't readable, but the IC interfacing the USB port is clearly from Realtek, while the other one seems to be the &lt;a href="https://web.archive.org/web/20220627071110/https://audiowise-t.com/"&gt;PAUxxxx SoCs from the defunct audiowise&lt;/a&gt; (Acquired by Airoha, a subsidiary of MediaTek).&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1807083928458551475-512" src="https://platform.twitter.com/embed/Tweet.html?id=1807083928458551475"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1807083928458551475-512');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1807083928458551475&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I didn't want to open the dongle, as opening it up is a little "destructive", but I couldn't resist the urge. So, after opening the dongle, I read the chips markings. The Realtek codec is the ALC4021 (which is used in &lt;a href="https://www.cabledo.com/google-usb-c-digital-to-3-5-mm-headphone-adapter-disassembly/"&gt;"similar" aplications&lt;/a&gt;), no datasheet available. The other is the PAU1823, again used in  &lt;a href="https://www.52audio.com/archives/88933.html"&gt;"similar" applications&lt;/a&gt; and no datasheet online(except one image and this &lt;a href="https://www.pcbbar.com/thread-35320-1-1.html"&gt;forum post,&lt;/a&gt; which I cannot access the downloads as it requires an account). The last chip is a 512kb SPI flash (the &lt;a href="https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/609/MX25V512E.pdf"&gt;MX25V512E&lt;/a&gt;), it can be for any of the other two chips.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6lh7z6835d9pghepjjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6lh7z6835d9pghepjjv.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5f54ojj0hl857bdzaxax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5f54ojj0hl857bdzaxax.png" alt="Image description" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju5jd7g27s5m8wgy0pqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju5jd7g27s5m8wgy0pqh.png" alt="Image description" width="730" height="858"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So it is basically a Bluetooth Audio Dongle. It seems that Lightspeed is just that, Bluetooth 5.2 with &lt;em&gt;their patented GreenRadio technology for low latency&lt;/em&gt;. But I'm not very sure.&lt;/p&gt;

&lt;p&gt;On the other hand, in the research I found a bunch of software programs for this dongle. There is the &lt;a href="https://support.logi.com/hc/en-us/articles/4407898529687-Download-G435-Gaming-Headset"&gt;official update software&lt;/a&gt;. There are also &lt;a href="https://www.reddit.com/r/LogitechG/comments/ykshdk/comment/k3q9t11/"&gt;these "unofficial" pairing(and more) tools&lt;/a&gt; on the Logitech Reddit. These tools seem widely used, as I found the logs of these on o&lt;a href="https://www.reddit.com/r/logitech/comments/17lykuk/g435_couldnt_connect_in_lightspeed/"&gt;ther Reddit posts&lt;/a&gt; and &lt;a href="https://github.com/duongchung1999/MerryLuxApp/blob/fe05ead83354d09db5424afa58058020759a87ff/Z_AllDll/DownloadAPI/DownloadAPI/bin/Debug/HDT647_160/log/20230318_text.log"&gt;even on GitHub!&lt;/a&gt;. Also, both the official and unofficial tools seem based on the same sources.&lt;/p&gt;

&lt;p&gt;Finally, I also found a&lt;a href="https://www.ebay.com/itm/166620468237"&gt; compatible dongle on eBay&lt;/a&gt;, which also provides a link to the previous "unofficial" tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The USB interface
&lt;/h2&gt;

&lt;p&gt;It has a good quantity of snd-usb-audio interfaces and an HID one.&lt;/p&gt;

&lt;p&gt;HID descriptor&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Logitech G series G435 Wireless Gaming Headset
# 0x05, 0x0c,                    // Usage Page (Consumer Devices)       0
# 0x09, 0x01,                    // Usage (Consumer Control)            2
# 0xa1, 0x01,                    // Collection (Application)            4
# 0x85, 0x03,                    //  Report ID (3)                      6
# 0x15, 0x00,                    //  Logical Minimum (0)                8
# 0x26, 0x8c, 0x02,              //  Logical Maximum (652)              10
# 0x19, 0x00,                    //  Usage Minimum (0)                  13
# 0x2a, 0x8c, 0x02,              //  Usage Maximum (652)                15
# 0x75, 0x10,                    //  Report Size (16)                   18
# 0x95, 0x02,                    //  Report Count (2)                   20
# 0x81, 0x00,                    //  Input (Data,Arr,Abs)               22
# 0xc0,                          // End Collection                      24
# 0x05, 0x0b,                    // Usage Page (Telephony Devices)      25
# 0x09, 0x01,                    // Usage (Phone)                       27
# 0xa1, 0x01,                    // Collection (Application)            29
# 0x85, 0x05,                    //  Report ID (5)                      31
# 0x15, 0x00,                    //  Logical Minimum (0)                33
# 0x26, 0xbf, 0x00,              //  Logical Maximum (191)              35
# 0x19, 0x00,                    //  Usage Minimum (0)                  38
# 0x2a, 0xbf, 0x00,              //  Usage Maximum (191)                40
# 0x95, 0x02,                    //  Report Count (2)                   43
# 0x81, 0x00,                    //  Input (Data,Arr,Abs)               45
# 0xc0,                          // End Collection                      47
# 0x06, 0xc0, 0xff,              // Usage Page (Vendor Usage Page 0xffc0) 48
# 0x09, 0x01,                    // Usage (Vendor Usage 0x01)           51
# 0xa1, 0x01,                    // Collection (Application)            53
# 0x06, 0xc1, 0xff,              //  Usage Page (Vendor Usage Page 0xffc1) 55
# 0x85, 0x06,                    //  Report ID (6)                      58
# 0x15, 0x00,                    //  Logical Minimum (0)                60
# 0x26, 0xff, 0x00,              //  Logical Maximum (255)              62
# 0x09, 0xf0,                    //  Usage (Vendor Usage 0xf0)          65
# 0x75, 0x08,                    //  Report Size (8)                    67
# 0x95, 0x07,                    //  Report Count (7)                   69
# 0x81, 0x02,                    //  Input (Data,Var,Abs)               71
# 0x09, 0xf1,                    //  Usage (Vendor Usage 0xf1)          73
# 0x75, 0x08,                    //  Report Size (8)                    75
# 0x96, 0x02, 0x02,              //  Report Count (514)                 77
# 0x91, 0x02,                    //  Output (Data,Var,Abs)              80
# 0x09, 0xf2,                    //  Usage (Vendor Usage 0xf2)          82
# 0x75, 0x08,                    //  Report Size (8)                    84
# 0x95, 0x3f,                    //  Report Count (63)                  86
# 0xb1, 0x02,                    //  Feature (Data,Var,Abs)             88
# 0xc0,                          // End Collection                      90
# 0x06, 0xc3, 0xff,              // Usage Page (Vendor Usage Page 0xffc3) 91
# 0x09, 0x01,                    // Usage (Vendor Usage 0x01)           94
# 0xa1, 0x01,                    // Collection (Application)            96
# 0x06, 0xc4, 0xff,              //  Usage Page (Vendor Usage Page 0xffc4) 98
# 0x85, 0x34,                    //  Report ID (52)                     101
# 0x15, 0x00,                    //  Logical Minimum (0)                103
# 0x26, 0xff, 0x00,              //  Logical Maximum (255)              105
# 0x09, 0xf3,                    //  Usage (Vendor Usage 0xf3)          108
# 0x75, 0x08,                    //  Report Size (8)                    110
# 0x95, 0x14,                    //  Report Count (20)                  112
# 0x81, 0x02,                    //  Input (Data,Var,Abs)               114
# 0x09, 0xf4,                    //  Usage (Vendor Usage 0xf4)          116
# 0x75, 0x08,                    //  Report Size (8)                    118
# 0x95, 0x02,                    //  Report Count (2)                   120
# 0x91, 0x00,                    //  Output (Data,Arr,Abs)              122
# 0xc0,                          // End Collection                      124

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The Consumer Control is for the volume up and down key press. The Telephony Devices I still cant find to trigger anything for this input event. And of course there are the Vendor Usage Page which are the most interesting ones. The usage of those is "strange", the only one used seems to be the &lt;strong&gt;report 6&lt;/strong&gt; in a curious way: The total size is completely ignored, usually sending 64 bytes of data. For writing, a standard &lt;strong&gt;send feature&lt;/strong&gt; is used, but for reading, some messages come as a input, so you have to listen to it, while others you have to issue a &lt;strong&gt;get feature&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reverse engineering the software
&lt;/h2&gt;

&lt;p&gt;Both the program and the &lt;code&gt;AwToolLIB&lt;/code&gt; are made in C# mono and shipped with some &lt;strong&gt;obfuscation&lt;/strong&gt;(.NET reactor). At first, I thought it wasn't obfuscated and lost a lot of time trying to understand fake symbols and hashed string constants. But after removing the string hashing with .NET reactor slayer and knowing some symbols were fake, it was relatively "easy" navigating the code with ILSpy. This code is pretty complete, containing all the classes to interface with their different devices and functions. It also includes some internal resources, including what seems to be the firmware (file &lt;code&gt;headset_RTK&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;
  
  
  Protocol
&lt;/h2&gt;

&lt;p&gt;The protocol can be divided into two layers. The first layer is used to communicate with the Realtek driver to send commands to the Audio SoC, possibly through one of its serial interfaces (most probable UART, but it can also be SPI or I2C). The second layer is these commands.&lt;/p&gt;
&lt;h3&gt;
  
  
  RTK communication protocol
&lt;/h3&gt;

&lt;p&gt;Messages are sent as a HID &lt;strong&gt;send feature&lt;/strong&gt;. On every message sent, an ack is received as a HID input event. For example, an OK ack looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Report ID&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;OK&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The messages I found so far are the following.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Message&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Enter Bootloader&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Exit Bootloader&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Send command&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CMD_LEN&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CMD_LEN&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The write command message is appended with the command at the end and requires entering the bootloader or strange and unusable behaviors will happen. Unfortunately, &lt;strong&gt;entering the bootloader will stop the audio functionalities&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Commands
&lt;/h3&gt;

&lt;p&gt;On top of the RTK protocol, the manufacturer has what it seems to be a proprietary command-response protocol. For sending a command, a &lt;strong&gt;RTK write&lt;/strong&gt; is used and for reading the response, a hid &lt;strong&gt;get feature&lt;/strong&gt; is used.&lt;/p&gt;

&lt;p&gt;Here is a quickly done dump of all commands I found. Keep in mind that some of these commands append arguments at the end, and surely I messed up writing some of them. Also, the bytes 4 is used to write the length in bytes of the command after this byte (sometimes is this length - 1). The byte 3, if 0 and the total command length is more than 5, is used as a command counter which gets incremented until 128.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;It is curious that all of them start with &lt;em&gt;[80,65]&lt;/em&gt;. Most of the commands seem to follow the following format, with a lot of exceptions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Always 80&lt;/td&gt;
&lt;td&gt;Always 65&lt;/td&gt;
&lt;td&gt;Command "category"&lt;/td&gt;
&lt;td&gt;Generally commands sent counter&lt;/td&gt;
&lt;td&gt;Generally length after this byte&lt;/td&gt;
&lt;td&gt;Command ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The commands category seems to be the following. Also, take it with a grain of salt.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Audio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;OTA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Customer custom commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;DSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Noise cancellation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;NVDS (Non-volatile memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Touch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Wireless connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;RF test&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The responses are another story that I might explore in a future blog post. The responses are read as 64 bytes blocks with an HID &lt;strong&gt;get feature&lt;/strong&gt; at the report 6.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bluetooth scan example
&lt;/h4&gt;

&lt;p&gt;As an example, the following python snippet does a Bluetooth scan, listing all devices found.  &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Pairing sequence
&lt;/h4&gt;

&lt;p&gt;Looking at the pairing software logs, I can extrapolate the following sequence to pair a dongle with a headset. To illustrate, let's say that the Bluetooth MAC address of the headsets is &lt;em&gt;[X0, X1, X2, X3, X4, X5]&lt;/em&gt;. The bytes relative to the standard command &lt;u&gt;are underlined&lt;/u&gt;. &lt;em&gt;Counter&lt;/em&gt; is the commands counter I mentioned on the protocol section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter Bootloader&lt;/li&gt;
&lt;li&gt;Sends LQ_DFU_BT_INQUIRY_SCAN (&lt;u&gt;80, 65, 14,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, 2, &lt;u&gt;240, 00&lt;/u&gt;) to enumerate all the Bluetooth devices.&lt;/li&gt;
&lt;li&gt;Sends TX_DG_CREATE_CONNECT_EX (&lt;u&gt;80, 65, 14,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, 9, &lt;u&gt;229, 00&lt;/u&gt;, &lt;em&gt;X5, X4, X3, X2, X1, X0&lt;/em&gt;) to pair the dongle with the headsets.&lt;/li&gt;
&lt;li&gt;Sends REMOTE_GET_CONNECT_STATUS (&lt;u&gt;80, 65, 14,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, 1, &lt;u&gt;227&lt;/u&gt;) to check that the pairing was successful. If it fails, it retries the previous step.&lt;/li&gt;
&lt;li&gt;Sends REMOTE_GET_MODE (&lt;u&gt;80, 65, 14,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, 1, &lt;u&gt;224&lt;/u&gt;) to get the "mode", maybe related to the connection mode on the headset (Bluetooth or lightspeed)?. If it is 01, it skips to the last step.&lt;/li&gt;
&lt;li&gt;Sends REMOTE_SET_MODE (&lt;u&gt;80, 65, 14,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, 2, &lt;u&gt;225&lt;/u&gt;, &lt;em&gt;01&lt;/em&gt;) to set the mode to 01.&lt;/li&gt;
&lt;li&gt;Sends REMOTE_GET_MODE (&lt;u&gt;80, 65, 14,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, 1, &lt;u&gt;224&lt;/u&gt;) to check that the mode has changed to 01.&lt;/li&gt;
&lt;li&gt;Sends OTA_CMD_GET_IMAGE_VERSION (&lt;u&gt;80, 65, 6,&lt;/u&gt; &lt;em&gt;counter&lt;/em&gt;, &lt;u&gt;4, 0, 52&lt;/u&gt;, 01, 77, 67) to checks that the headset and the update tool (I think) firmware version are the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  So, can we read the battery level?
&lt;/h3&gt;

&lt;p&gt;Yes, but as it requires sending a command, so entering the bootloader, a cut on the audio will be produced which is bothering. Maybe this is why Logitech doesn't support these headphones on their software programs?. Here was where I lost interest in this, as the main reason I started researching about these headphones was for knowing the battery level. There are a lot of commands and fun things still worth exploring, which can result in interesting information for implementing an app for these headsets, but this is it for now.&lt;/p&gt;

</description>
      <category>usb</category>
      <category>reverseengineering</category>
      <category>hardware</category>
      <category>reversing</category>
    </item>
    <item>
      <title>USB HID Down the rabbit hole: Reverse engineering the Logitech CU0019 USB receiver</title>
      <dc:creator>endes0</dc:creator>
      <pubDate>Thu, 30 May 2024 09:04:12 +0000</pubDate>
      <link>https://dev.to/endes/usb-hid-down-the-rabbit-hole-reverse-engineering-the-logitech-cu0019-usb-receiver-4d6a</link>
      <guid>https://dev.to/endes/usb-hid-down-the-rabbit-hole-reverse-engineering-the-logitech-cu0019-usb-receiver-4d6a</guid>
      <description>&lt;p&gt;Recently, I have an obsession with USB HID devices. So let's go through this rabbit hole. These devices send a descriptor which specifies the format of the "packets" they send or receive. Most fields are defined in the standard, so your hid driver can interact with the device out of the box. Some devices implement curious fields, like my headphones can send phone "events" like calls.&lt;br&gt;
Most of them implement custom vendor specific fields. This is where it gets interesting.&lt;/p&gt;

&lt;p&gt;Let's introduce my laptop mouse (below is Jaime playing with it), it's a Logitech M185. Almost all the Logitech hid devices implement their proprietary standard hid++, which is well-known. Mine is the exception.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmitc2mfw3u1noax1vsiq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmitc2mfw3u1noax1vsiq.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It uses the &lt;a href="https://support.logi.com/hc/en-us/articles/7565042135959-USB-Wireless-Receiver-Pairing-and-Troubleshooting#h_01G8P2H7084PDDFCXE3QN9GM15" rel="noopener noreferrer"&gt;CU0019 dongle&lt;/a&gt; (aka PID C542), &lt;a href="https://fccid.io/JNZCU0019/Internal-Photos/Internal-photos-4169246" rel="noopener noreferrer"&gt;here are some internal photos thanks to the FCC&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1v2zei33ow6vf5t9vqgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1v2zei33ow6vf5t9vqgl.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/pwr-Solaar/Solaar/issues/1835" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Nano Receiver with USB ID C542 does not use HID++
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1835&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/yesimxev" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F23633302%3Fv%3D4" alt="yesimxev avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/yesimxev" rel="noopener noreferrer"&gt;yesimxev&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/pwr-Solaar/Solaar/issues/1835" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 10, 2022&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;strong&gt;Dmesg log&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[21837.663373] usb 2-2: new full-speed USB device number 9 using xhci_hcd
[21837.815411] usb 2-2: New USB device found, idVendor=046d, idProduct=c542, bcdDevice= 3.02
[21837.815419] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[21837.815422] usb 2-2: Product: Wireless Receiver
[21837.815424] usb 2-2: Manufacturer: Logitech
[21837.819645] input: Logitech Wireless Receiver Mouse as /devices/pci0000:00/0000:00:14.0/usb2/2-2/2-2:1.0/0003:046D:C542.000D/input/input38
[21837.819801] hid-generic 0003:046D:C542.000D: input,hidraw0: USB HID v1.11 Mouse [Logitech Wireless Receiver] on usb-0000:00:14.0-2/input0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Describe the bug&lt;/strong&gt;
Adapter CU0019 not recognised (same on windows Logitech utilities). Tried multiple of these adapters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To Reproduce&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Plug in&lt;/li&gt;
&lt;li&gt;Start solaar&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Screenshots&lt;/strong&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/23633302/201109171-8137385e-5576-446b-bd65-93278ec2ab26.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F23633302%2F201109171-8137385e-5576-446b-bd65-93278ec2ab26.png" alt="Screenshot_2022-11-10_13-49-26"&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pwr-Solaar/Solaar/issues/1835" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It uses the &lt;a href="https://wiki.telink-semi.cn/wiki/chip-series/TLSR836x-Series/" rel="noopener noreferrer"&gt;Telink TLSR8366&lt;/a&gt;. While Telink openly publishes a lot of tools, documents and code on their website, it is an amalgam of things which don't do a good job explaining themselves, so most of the time I was like "huh?". Oh, they also implement their own ISA called TC32, which is undocumented. Fortunately, GitHub users &lt;em&gt;trust1995&lt;/em&gt; and &lt;em&gt;rgov&lt;/em&gt; have done a fairly decent job of implementing it on Ghidra.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/trust1995" rel="noopener noreferrer"&gt;
        trust1995
      &lt;/a&gt; / &lt;a href="https://github.com/trust1995/Ghidra_TELink_TC32" rel="noopener noreferrer"&gt;
        Ghidra_TELink_TC32
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Ghidra processor specification for the Telink TC32
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Telink TC32 Processor Specification for Ghidra&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This repository contains a fairly complete processor specification for the Telink TC32 architecture, used by all of Telink's System-On-Chips. The work herein is based on Ryan Govostes' work and extended with various fix-ups and actual P-code implementation.&lt;/p&gt;
&lt;p&gt;Right now decompilation is working well with several tested TC32 ELFs.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Copy the  &lt;code&gt;Telink_TC32&lt;/code&gt; repository to &lt;code&gt;Ghidra/Processors&lt;/code&gt;. Restart Ghidra
Afterwards, when importing a TC32 binary, when prompted for the binary's "Language", select the "Telink_TC32" processor.&lt;/p&gt;
&lt;p&gt;For analysing Telink ELFs, I use the following process (using some plugins from my GhidraPlugins repo):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Import the binary, do not Auto-Analyse&lt;/li&gt;
&lt;li&gt;Run the fix_funcnames.py plugin&lt;/li&gt;
&lt;li&gt;Run the disas_symbols.py plugin&lt;/li&gt;
&lt;li&gt;Run the Auto-Analysis, without call convention identification&lt;/li&gt;
&lt;li&gt;Parse the register header file into the Data Type Manager (Grab the Telink SDK, redefine the REG_ADDR%X macros in register_82XX.h as integers instead of as pointers)&lt;/li&gt;
&lt;li&gt;Export the register/defines values just imported from…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/trust1995/Ghidra_TELink_TC32" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The HID descriptor, apart from the standard mouse report, exposes a vendor report with ID 5. This report has a feature (aka input and output interface) of 7 bytes. Reading from it returns nothing.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

0x90,              // Output
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x02,        // Usage (Mouse)
0xA1, 0x01,        // Collection (Application)
0x85, 0x01,        //   Report ID (1)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x05, 0x09,        //     Usage Page (Button)
0x19, 0x01,        //     Usage Minimum (0x01)
0x29, 0x05,        //     Usage Maximum (0x05)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x95, 0x05,        //     Report Count (5)
0x75, 0x01,        //     Report Size (1)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x03,        //     Report Size (3)
0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x16, 0x01, 0x80,  //     Logical Minimum (-32767)
0x26, 0xFF, 0x7F,  //     Logical Maximum (32767)
0x75, 0x10,        //     Report Size (16)
0x95, 0x02,        //     Report Count (2)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x38,        //     Usage (Wheel)
0x15, 0x81,        //     Logical Minimum (-127)
0x25, 0x7F,        //     Logical Maximum (127)
0x75, 0x08,        //     Report Size (8)
0x95, 0x01,        //     Report Count (1)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x00,        // Usage (Undefined)
0xA1, 0x01,        // Collection (Application)
0x85, 0x05,        //   Report ID (5)
0x06, 0x00, 0xFF,  //   Usage Page (Vendor Defined 0xFF00)
0x09, 0x01,        //   Usage (0x01)
0x15, 0x81,        //   Logical Minimum (-127)
0x25, 0x7F,        //   Logical Maximum (127)
0x75, 0x08,        //   Report Size (8)
0x95, 0x07,        //   Report Count (7)
0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection

// 91 bytes


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  Getting a firmware dump
&lt;/h1&gt;

&lt;p&gt;I started the house by the roof, instead of trying to fuzz the vendor report, I tried to dump the firmware "the hardware way". These chips have a SWIRE pin, which is a proprietary protocol for debugging and accessing the memory. This interface requires a Telink EVK tool, which isn't cheap, but GitHub user &lt;em&gt;pvvx&lt;/em&gt; has written some tools for reading and writing the memory. The 826X tools are compatible with the 836x series.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pvvx" rel="noopener noreferrer"&gt;
        pvvx
      &lt;/a&gt; / &lt;a href="https://github.com/pvvx/TlsrComSwireWriter" rel="noopener noreferrer"&gt;
        TlsrComSwireWriter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      TLSR826x/825x COM port Swire Writer
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;TlsrComSwireWriter&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;TLSR826x/825x COM port Swire Writer Utility&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Telink SWIRE simulation on a COM port.&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;Using only the COM port, downloads and runs the program in SRAM for TLSR826x or TLSR825x chips.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/pvvx/TlsrComSwireWriter/blob/master/schematicc.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fpvvx%2FTlsrComSwireWriter%2Fraw%2Fmaster%2Fschematicc.gif" alt="SCH"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;COM-RTS connect to Chip RST or Vcc.&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;usage: ComSwireWriter [-h] [--port PORT] [--tact TACT] [--file FILE] [--baud BAUD]

TLSR826x ComSwireWriter Utility version 21.02.20

optional arguments:
    -h, --help            show this help message and exit
    --port PORT, -p PORT  Serial port device (default: COM1)
    --tact TACT, -t TACT  Time Activation ms (0-off, default: 600 ms)
    --file FILE, -f FILE  Filename to load (default: floader.bin)
    --baud BAUD, -b BAUD  UART Baud Rate (default: 230400)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Added TLSR825xComFlasher:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;usage: TLSR825xComFlasher.py [-h] [-p PORT] [-t TACT] [-c CLK] [-b BAUD] [-r]
                             [-d]
                             {rf,wf,es,ea}
TLSR825x Flasher version 00.00.02

positional arguments:
  {rf,wf,es,ea}         TLSR825xComFlasher {command} -h for additional help
    rf                  Read Flash to binary file
    wf                  Write file to Flash with sectors erases
    es                  Erase Region (sectors)&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pvvx/TlsrComSwireWriter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;After adapting the tools to use my FTDI ft2332HL board and dumps the memory and soldering cables to the reset and Swire pin (fortunately these pins aren't connected to anything), the dumper kinda worked. It was very unstable, but the data seemed good (it wasn't). Seeing the &lt;code&gt;KNLT&lt;/code&gt;, &lt;code&gt;Logitech&lt;/code&gt;, &lt;code&gt;Wirless Receiver&lt;/code&gt; and &lt;code&gt;TLSR8366&lt;/code&gt; strings was a good confirmation that it wasn't just garbage.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1793635090746257531-109" src="https://platform.twitter.com/embed/Tweet.html?id=1793635090746257531"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1793635090746257531-109');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1793635090746257531&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpdbsmtgm8ortfk2rex3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpdbsmtgm8ortfk2rex3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5s6clzne9076ljqsd59m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5s6clzne9076ljqsd59m.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I could have wasted countless days trying to work with this dump, hopefully, I decided to do things right and started fuzzing the HID vendor report using a fastly written Python script. The results were, let's say, very verbose. A lot of send commands responded with data. Also, I thought I bricked the device as the mouse stopped working, luckily a reset fixed it. &lt;/p&gt;

&lt;p&gt;On the results instantaneously something catches my attention. When the fuzzer was changing the data of the second byte, the received data was like sliding.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

i: -064 j: -082 send dat: [5, 192, 174, 0, 0, 0, 0, 0]   recv dat: [0, 0, 0, 128, 3, 8, 33, 132]
i: -064 j: -081 send dat: [5, 192, 175, 0, 0, 0, 0, 0]   recv dat: [0, 0, 128, 5, 3, 8, 33, 132]
i: -064 j: -080 send dat: [5, 192, 176, 0, 0, 0, 0, 0]   recv dat: [0, 128, 5, 0, 3, 8, 33, 132]
i: -064 j: -079 send dat: [5, 192, 177, 0, 0, 0, 0, 0]   recv dat: [128, 5, 0, 0, 3, 8, 33, 132] 
i: -064 j: -078 send dat: [5, 192, 178, 0, 0, 0, 0, 0]   recv dat: [5, 0, 0, 0, 3, 8, 33, 132] 
i: -064 j: -077 send dat: [5, 192, 179, 0, 0, 0, 0, 0]   recv dat: [0, 0, 0, 0, 3, 8, 33, 132] 
i: -064 j: -076 send dat: [5, 192, 180, 0, 0, 0, 0, 0]   recv dat: [0, 0, 0, 0, 3, 8, 33, 132]  
i: -064 j: -075 send dat: [5, 192, 181, 0, 0, 0, 0, 0]   recv dat: [0, 0, 0, 147, 3, 8, 33, 132] 
i: -064 j: -074 send dat: [5, 192, 182, 0, 0, 0, 0, 0]   recv dat: [0, 0, 147, 0, 3, 8, 33, 132]    
i: -064 j: -073 send dat: [5, 192, 183, 0, 0, 0, 0, 0]   recv dat: [0, 147, 0, 0, 3, 8, 33, 132]
i: -064 j: -072 send dat: [5, 192, 184, 0, 0, 0, 0, 0]   recv dat: [147, 0, 0, 0, 3, 8, 33, 132] 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;My intuition was that we were reading memory!. I quickly write a dumper and Tachan! We got a more stable, easy and correct dump.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1794070181775327239-961" src="https://platform.twitter.com/embed/Tweet.html?id=1794070181775327239"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1794070181775327239-961');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1794070181775327239&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Beyond are the scripts I made for dumping, they are crappy and require the &lt;a href="https://gitlab.freedesktop.org/libevdev/hid-tools" rel="noopener noreferrer"&gt;hid-tools package&lt;/a&gt;. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h1&gt;
  
  
  Analyzing the firmware dump
&lt;/h1&gt;

&lt;p&gt;After renaming the symbols of the startup code according to the names on the boot c startup assembly code of the SDKs, I found that a constant was different from those. After a quick GitHub code search, VOILA here are the possible SDK they used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexu4rgzk4khcs1ksxi2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexu4rgzk4khcs1ksxi2l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3h64ao0aegtrdm3g8u5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3h64ao0aegtrdm3g8u5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55y1nq7m6cr99were35m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55y1nq7m6cr99were35m.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm lazy, I wanted to generate a Fidb for Ghidra to automatically recognize functions from the SDK, especially the ones related to USB, but I didn't want to setup and compile the SDK. Fortunately, &lt;a href="https://github.com/stephanosio/js_km_proj/blob/4d9960bace457d7514554185ee73924662cfabcd/tlsdk/8366_dongle/8366_dongle.lst" rel="noopener noreferrer"&gt;there was a precompiled bin with its symbols on one of the repos&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A great find
&lt;/h2&gt;

&lt;p&gt;After generating a Fidb and doing an analysis, for me, it was clear to me that the firmware was a slightly modified version of the &lt;em&gt;8366_dongle&lt;/em&gt; project on that repo(I already had my suspicions at the moment I opened the disassembly of the bin). &lt;/p&gt;

&lt;p&gt;After a little bit of digging, tachan! This seems to be the code that handles the vendor HID Report.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Cpp
        case HID_REPORT_CUSTOM:
#if (USB_CUSTOM_HID_REPORT)
        {   //Paring, EMI-TX, EMI-RX
            if (data_request) {
                int i=0;
                usbhw_reset_ctrl_ep_ptr (); //address
                for(i=0;i&amp;lt;8;i++) {
                    host_cmd[i] = usbhw_read_ctrl_ep_data();
                }
#if (USB_CUSTOM_HID_REPORT_REG_ACCESS)
                custom_reg_cmd = (host_cmd[1] &amp;amp; 0xf0) == 0xc0;
                if (custom_reg_cmd) {
                    host_cmd[0] = 0;
                    int adr = *((u16 *)(host_cmd + 2));
                    int len = host_cmd[1] &amp;amp; 3;
                    if (host_cmd[1] == 0xcc &amp;amp;&amp;amp; adr == 0x5af0) { //re-enumerate device
                        usb_dp_pullup_en (0);           //disable device
                        sleep_us (300000);
                        reg_ctrl_ep_irq_mode = 0xff;    //hardware mode
                        usb_dp_pullup_en (1);           //enable device
                    }
                    else {
                        adr += 0x800000;
                    }

                    if ((host_cmd[1] &amp;amp; 0x0c)==0) {  //write core register
                        if (len == 0) {
                            for (int k=0; k&amp;lt;4; k++) {
                                custom_read_dat = (custom_read_dat &amp;gt;&amp;gt; 8) | (read_reg8 (adr++) &amp;lt;&amp;lt; 24);
                            }
                        }
                        else if (len == 1) {
                            write_reg8 (adr, host_cmd[4]);
                        }
                        else if (len == 2) {
                            write_reg16 (adr, *((u16 *)(host_cmd + 4)));
                        }
                        else {
                            write_reg32 (adr, *((u32 *)(host_cmd + 4)));
                        }
                    }
                    else {  //read core register
                        if (len == 0) {
                            custom_read_dat = analog_read (host_cmd[2]);
                        }
                        else {
                            analog_write (host_cmd[2], host_cmd[4]);
                        }
                    }
                }

...

        case HID_REQ_GetReport:
#if(USB_SOMATIC_ENABLE)
            if(usbsomatic_hid_report_type((control_request.wValue &amp;amp; 0xff))){
            }
            else
#elif (USB_CUSTOM_HID_REPORT)
            if( control_request.wValue==0x0305 ) {
                if (USB_CUSTOM_HID_REPORT_REG_ACCESS &amp;amp;&amp;amp; custom_reg_cmd) {
                    usbhw_write_ctrl_ep_data (custom_read_dat);
                    usbhw_write_ctrl_ep_data (custom_read_dat&amp;gt;&amp;gt;8);
                    usbhw_write_ctrl_ep_data (custom_read_dat&amp;gt;&amp;gt;16);
                    usbhw_write_ctrl_ep_data (custom_read_dat&amp;gt;&amp;gt;24);
                    usbhw_write_ctrl_ep_data (0x10);
                    usbhw_write_ctrl_ep_data (0x20);
                    usbhw_write_ctrl_ep_data (0x40);
                    usbhw_write_ctrl_ep_data (0x80);
                }
                else {
                    usbhw_write_ctrl_ep_data (0x04);
                    usbhw_write_ctrl_ep_data (0x58);
                    usbhw_write_ctrl_ep_data (0x00);
                    usbhw_write_ctrl_ep_data (host_cmd_paring_ok ? 0xa1 : 0x00);  //For binding OK
                    usbhw_write_ctrl_ep_data (0x00);
                    usbhw_write_ctrl_ep_data (0x00);
                    usbhw_write_ctrl_ep_data (0x08);
                    usbhw_write_ctrl_ep_data (0x00);
                }
            }
            else
#endif          
            {   //  donot know what is this
    //          usbhw_write_ctrl_ep_data(0x81);
    //          usbhw_write_ctrl_ep_data(0x02);
    //          usbhw_write_ctrl_ep_data(0x55);
    //          usbhw_write_ctrl_ep_data(0x55);
            }
            break;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;em&gt;/proj/drivers/usb.c&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Cpp
void usb_host_cmd_proc(u8 *pkt)
{
    extern u8       host_cmd[8];
    extern u8       host_cmd_paring_ok;

    u8   chn_idx;
    u8   test_mode_sel;
    u8   cmd = 0;
    static emi_flg;


    if((host_cmd[0]==0x5) &amp;amp;&amp;amp; (host_cmd[2]==0x3) )
    {
        host_cmd[0] = 0;
        dongle_host_cmd1 = host_cmd[1];

        if (dongle_host_cmd1 &amp;gt; 12 &amp;amp;&amp;amp; dongle_host_cmd1 &amp;lt; 16){  //soft paring
            host_cmd_paring_ok = 0;
            rf_paring_tick = clock_time();  //update paring time

            if(dongle_host_cmd1 == 13){     //kb and mouse tolgether
                mouse_paring_enable = 1;
                keyboard_paring_enable = 1;
            }
            else if(dongle_host_cmd1 == 14){ //mouse only
                mouse_paring_enable = 1;
            }
            else if(dongle_host_cmd1 == 15){  //keyboard only
                keyboard_paring_enable = 1;
            }
        }
        else if(dongle_host_cmd1 &amp;gt; 0 &amp;amp;&amp;amp; dongle_host_cmd1 &amp;lt; 13)  //1-12:����EMI
        {
            emi_flg = 1;
            cmd = 1;

            irq_disable();
            reg_tmr_ctrl &amp;amp;= ~FLD_TMR1_EN;
            //rf_stop_trx ();

            chn_idx = (dongle_host_cmd1-1)/4;
            test_mode_sel = (dongle_host_cmd1-1)%4;
        }
    }

    if(emi_flg){
        emi_process(cmd, chn_idx,test_mode_sel, pkt, dongle_cust_tx_power_emi);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;em&gt;/vendor/dongle/dongle_emi.c&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Mouse Device ID
&lt;/h2&gt;

&lt;p&gt;I think I also found the memory address where the current paired Mouse is stored(&lt;code&gt;custom_binding[0]&lt;/code&gt;): &lt;code&gt;0x809160&lt;/code&gt;. I can't confirm it as I don't have another mouse and the value is a little bit off for me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nvfbxvm5iuctuqoydix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nvfbxvm5iuctuqoydix.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Potentially, this can be used to send a USB HID read memory and obtain the current mouse ID.&lt;/p&gt;
&lt;h2&gt;
  
  
  Ghidra symbols
&lt;/h2&gt;

&lt;p&gt;Here are all the symbols I found, it can be imported with the &lt;code&gt;ImportSymbolsScript.py&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h1&gt;
  
  
  USB HID custom commands
&lt;/h1&gt;

&lt;p&gt;So, after analyzing the firmware, fuzzer output and doing some test, I found the following USB HID set feature commands.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;4&lt;/th&gt;
&lt;th&gt;5&lt;/th&gt;
&lt;th&gt;6&lt;/th&gt;
&lt;th&gt;7&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0xD&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Software pairing: Mouse and keyboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xE&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Software pairing: Mouse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xF&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Software pairing: Keyboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x1&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel low, mode carrier&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x2&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel low, mode cd&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel low, mode  rx&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x4&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel low, mode tx&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x5&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel medium, mode carrier&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x6&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel medium, mode cd&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x7&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel medium, mode  rx&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x8&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel medium, mode tx&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x9&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel high, mode carrier&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xA&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel high, mode cd&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xB&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel high, mode  rx&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC&lt;/td&gt;
&lt;td&gt;0x3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;EMI: channel high, mode tx&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC0&lt;/td&gt;
&lt;td&gt;addr&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(addr&amp;gt;&amp;gt;8)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Memory: read 32 bits from &lt;strong&gt;addr&lt;/strong&gt; + 0x800000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;addr&lt;/strong&gt;&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;addr&lt;/strong&gt;&amp;gt;&amp;gt;8)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;dat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Memory: write 8 bits &lt;strong&gt;dat&lt;/strong&gt; to &lt;strong&gt;addr&lt;/strong&gt; + 0x800000&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;addr&lt;/strong&gt;&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;addr&lt;/strong&gt;&amp;gt;&amp;gt;8)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;dat&lt;/strong&gt;&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;dat&lt;/strong&gt;&amp;gt;&amp;gt;8)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Memory: write 16 bits &lt;strong&gt;dat&lt;/strong&gt; to &lt;strong&gt;addr&lt;/strong&gt; + 0x800000&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;addr&lt;/strong&gt;&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;addr&lt;/strong&gt;&amp;gt;&amp;gt;8)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;dat&lt;/strong&gt;&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;dat&lt;/strong&gt;&amp;gt;&amp;gt;8)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;dat&lt;/strong&gt;&amp;gt;&amp;gt;16)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;(&lt;strong&gt;dat&lt;/strong&gt;&amp;gt;&amp;gt;24)&amp;amp;0xff&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Memory: write 32 bits &lt;strong&gt;dat&lt;/strong&gt; to &lt;strong&gt;addr&lt;/strong&gt; + 0x800000&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;addr&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Memory: read analog address &lt;strong&gt;addr&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;addr&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;dat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Memory:  write 8 bits &lt;strong&gt;dat&lt;/strong&gt; at analog address &lt;strong&gt;addr&lt;/strong&gt;&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xCC&lt;/td&gt;
&lt;td&gt;0xF0&lt;/td&gt;
&lt;td&gt;0x5A&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Misc: "renumerates USB devices"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notes: Take the &lt;em&gt;italics&lt;/em&gt; entries with a grain of salt, as I didn't test it. Byte 0 is always the report ID, in this case 5.&lt;/p&gt;

&lt;p&gt;It seems that software pairing is broken, Keyboard and Mouse pairing command always return success while the other 2 never succeed. Also, all the pairing commands disconnect the mouse, and it won't work until restarting the dongle.&lt;/p&gt;

&lt;p&gt;Issuing the "renumerate" command will connect the device as a USB printer "Telink Semiconductor USB DevSys" with VID 248A and PID 5320. Maybe this is the "USB programming mode" for interfacing with &lt;a href="https://wiki.telink-semi.cn/wiki/IDE-and-Tools/Burning-and-Debugging-Tools-for-all-Series/" rel="noopener noreferrer"&gt;Telink BDT tools&lt;/a&gt;? Taking a look at the sources of &lt;a href="https://wiki.telink-semi.cn/debug/readme/README_en.html#3" rel="noopener noreferrer"&gt;web BDT tool&lt;/a&gt;, the PID doesn't seem to match. So I thought it wasn't.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfizqqgrh0p91484dasc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfizqqgrh0p91484dasc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Javascript
async function  usb_connect(){
    const myfilters = [
      { 'vendorId': 0x2341, 'productId': 0x8036 },
      { 'vendorId': 0x248A, 'productId': 0x826A }, ]; //'productId': 0x826A 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The analog read I think it reads the "3.3V analog registers" referenced in the &lt;a href="https://wiki.telink-semi.cn/doc/ds/DS_TLSR8366-E_Datasheet%20for%20Telink%202.4GHz%20RF%20System-On-Chip%20Solution%20TLSR8366.pdf" rel="noopener noreferrer"&gt;datasheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mn1r76fqjb14ljh1u74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mn1r76fqjb14ljh1u74.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Another "great" dump
&lt;/h3&gt;

&lt;p&gt;Before I said that when the device renumerates as "Telink Semiconductor USB DevSys" maybe it is for the BDT tools, well after launching the desktop tools on Windows, it connects, so it is.&lt;/p&gt;

&lt;p&gt;This is great as we can have total access to all the memory spaces and also some debugging functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuztsoffw6uqh51ulpiig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuztsoffw6uqh51ulpiig.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9eigcva32f5dmpod24j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9eigcva32f5dmpod24j4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's just say that the memory access tool, well, umh, it's not great. The CORE access it also seems to start at address 0x800000, but if we read 0x808000 it seems to contain errors, or maybe another program.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnhdbkh4x9fw3f1rgdm6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnhdbkh4x9fw3f1rgdm6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The analog read works like the USB HID analog read, the flash read always returns 0xFF (In theory this chip doesn't have flash at all) and unfortunately the OTP read doesn't work. This is bad news, as the OTP memory is likely to have something.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjea0qcsvlkzwort6hi2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjea0qcsvlkzwort6hi2r.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>usb</category>
      <category>reverseengineering</category>
      <category>hardware</category>
      <category>reversing</category>
    </item>
  </channel>
</rss>
