gl2gb/public/posts/reverse-engineering-of-the-.../index.html

407 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="en"><head>
<title>GoatPr0n.farm</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="format-detection" content="telephone=no" />
<meta name="theme-color" content="#000084" />
<link rel="icon" href="https://goatpr0n.farm//favicon.ico">
<link rel="canonical" href="https://goatpr0n.farm/">
<link rel="stylesheet" href="/css/bootstrap.css">
<link rel="stylesheet" href="/css/bootstrap-responsive.css">
<link rel="stylesheet" href="/css/style.css">
</head><body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"></button>
<a class="brand" href="https://goatpr0n.farm/">GoatPr0n.farm</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li>
<a href="/posts/">
<span>All posts</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</nav><div id="content" class="container">
<div class="row-fluid navmargin">
<div class="page-header">
<h1>Reverse Engineering of the SkyRC MC3000 Battery Charger USB Protocol - Mon, Mar 18, 2019</h1>
</div>
<p class="lead"></p>
<h2 id="software-requirements">Software Requirements</h2>
<p>Decompiler for .NET programs</p>
<ul>
<li><a href="https://www.jetbrains.com/decompiler/">dotPeek</a></li>
</ul>
<p>The implementation of the protocol is then written in <em>Python</em>. Let&rsquo;s hear what the curator has to say:</p>
<p>Tell me about <em>Python</em>.</p>
<pre><code>&gt; Wow. Much Snake. Easy programming!
&gt;
&gt; \- Doge
</code></pre>
<p>Tell me about <em>dotPeek</em>.</p>
<pre><code>&gt; Easy decompilation. Readable syntax. Very easy.
&gt;
&gt; \- Doge
</code></pre>
<h2 id="analyzing-mc3000_monitor">Analyzing MC3000_Monitor</h2>
<h3 id="decompiling">Decompiling</h3>
<p>Start <em>dotPeek</em> and use Drag&rsquo;n&rsquo;Drop to load the Application. Or uSe <strong>CtRL+O</strong> anD LoCATe tHe fILe uSiNg ThE bOrwsEr. I am not your mother! The file will show up in the <em>Assembly Explorer</em>. You can expand and collapse nodes within the tree. Familiarize yourself with the program structure. Try to find the entry point or other &ldquo;important&rdquo; functions, modules or resources.</p>
<p><img src="https://dl.goatpr0n.events/$/dQCmQ" alt=""></p>
<p>If you right click the <strong>MC3000_Monitor</strong> node you can export the project with individual source files. This project is stored as <em>*.cs</em> source files.</p>
<p>You should now have either the project loaded into <em>dotPeek</em> or - in addition - saved it as project and/or loaded it into <em>Visual Studio (VS)</em> (Not covered here). I cannot afford <em>VS</em>. Still saving money to upgrade my IDA Pro license.</p>
<pre><code> Intermission:
This is the curator speaking, and I command you to stop whining, 'JPK'.
</code></pre>
<p>As you can see, a lot of professionalism.</p>
<h3 id="exploring-the-code">Exploring the code</h3>
<p>For me the easiest way to begin, is to find parts of code where user interaction is required. Run the program and look at the user interface. In this particular case we have four buttons next to a label each.</p>
<p>Lets explore the code in <em>dotPeek</em> and see if we can find some code that might look familiar.</p>
<p><img src="https://dl.goatpr0n.events/$/yWSxh" alt=""></p>
<p>Pressing one of the buttons opens another window where you can configure the charger slot. By further reading through the different functions you might come across the function <code>InitializeComponents():void</code>. Each window element gets setup and function callbacks/events are registered.</p>
<p>You eventually find something like this (see the picture below).</p>
<p><img src="https://dl.goatpr0n.events/$/lRBYC" alt=""></p>
<p>Let&rsquo;s put on our smart looking spec ticals and read line 3468 and 3470. Line 3468 is the creation of the button text, which should look familiar. If not, search for hints on <a href="https://goatpr0n.farm">this page</a>. Line 3470 binds a function to the button press. With a <strong>Ctrl+Left click</strong> we jump to the function definition in <em>dotPeek</em>.</p>
<p>The function <code>private void button1_Click_1(object sender, EventArgs e)</code> is pretty simple to read. When the button is clicked, get the button name (e.g. &ldquo;button1&rdquo; [1]) and check if there is either the number <code>1</code>, <code>2</code>, <code>3</code> or <code>4</code> in the name.</p>
<p><img src="https://dl.goatpr0n.events/$/Wul71" alt=""></p>
<p>Can you see the problem here? There is no error handling if button number is smaller than one or greater four. As an array is indexed, the program will probably crash. At this point we don&rsquo;t care. We want to make our own library, to make it better or different. After the name checking to know which slot is addressed, it calls a function <code>public void Set_Battery_Type_Caps(ChargerData[]data, int index)</code>. The functions sets the parameters of each battery slot and saves the values to an array.</p>
<p>This function sums up all parameters we need to know to setup a charger slot by or self. And we now know the default values. The below listing is the exception code, if anything goes wrong in the code above, but not when using an index outside bounds.</p>
<pre><code>// Battery.cs:1195
data[index].Type = 0;
data[index].Caps = 2000;
data[index].Mode = 0;
data[index].Cur = 1000;
data[index].dCur = 500;
data[index].End_Volt = 4200;
data[index].Cut_Volt = 3300;
data[index].End_Cur = 100;
data[index].End_dCur = 400;
data[index].Cycle_Mode = 0;
data[index].Cycle_Count = 1;
data[index].Cycle_Delay = 10;
data[index].Peak_Sense = 3;
data[index].Trickle = 50;
data[index].Hold_Volt = 4180;
data[index].CutTemp = 450;
data[index].CutTime = 180;
</code></pre>
<p>The data structure <code>ChargerData</code> can be looked up as well, but the above listing is a little bit easier to read.</p>
<p>What we haven&rsquo;t seen at this point were bytes transferred to or from the device.</p>
<p><img src="https://dl.goatpr0n.events/$/DVfA2" alt=""></p>
<p>At this point, there are multiple ways to get a good starting point on finding the functions where data is transmitted or received. One option is to look at the Assembly Explorer again for functions names of possible interest.</p>
<p><img src="https://dl.goatpr0n.events/$/WJAOM" alt=""></p>
<p>These convenient looking functions. Or should I say obvious function names are obvious, are used to handle the USB device communication. Try right clicking a function to find out where it is used. I have used <code>usbOnDataRecieved</code>. In the below window with search results you can find a reference located in the constructor [2] of the class <code>FormLoader</code>.</p>
<pre><code>// FormLoader.cs:352
this.usb = new UsbHidPort();
this.usb.ProductId = 1;
this.usb.VendorId = 0;
this.usb.OnSpecifiedDeviceArrived += new EventHandler(this.usbOnSpecifiedDeviceArrived);
this.usb.OnSpecifiedDeviceRemoved += new EventHandler(this.usbOnSpecifiedDeviceRemoved);
this.usb.OnDeviceArrived += new EventHandler(this.usbOnDeviceArrived);
this.usb.OnDeviceRemoved += new EventHandler(this.usbOnDeviceRemoved);
this.usb.OnDataRecieved += new DataRecievedEventHandler(this.usbOnDataRecieved);
this.usb.OnDataSend += new EventHandler(this.usbOnDataSend);
</code></pre>
<p>These lines above register event handlers with an instance of <code>UsbHidPort</code>. An event might be connecting or disconnecting the device (line: 355-358) or transferred data (line: 359-360). There is nothing special about the connect functions, except for <code>usbOnSpecifiedDevice...</code> ones. There is a call to stop and stop the <code>timer2</code> instance. We will look at this object in a second, but first we have a look at <code>usbOnDataSend</code> and <code>usbOnDataRecieved</code>.</p>
<pre><code>// FormLoader.cs:513
private void usbOnDataRecieved(object sender, DataRecievedEventArgs args)
{
if (this.InvokeRequired)
{
try
{
this.Invoke((Delegate) new DataRecievedEventHandler(this.usbOnDataRecieved), sender, (object) args);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
else
{
++this.packet_counter;
for (int index = 0; index &lt; 65; ++index)
this.inPacket[index] = args.data[index];
this.dataReceived = true;
}
}
...
// FormLoader.cs:580
private void usbOnDataSend(object sender, EventArgs e)
{
this.Text = &quot;ChargeMonitor V2 Connect&quot;;
this.label_usb_status.Text = &quot;USB ON&quot;;
this.label_usb_status.ForeColor = Color.Green;
}
</code></pre>
<p>The <code>usbOnDataSend</code> function is boring, and we can ignore it. There is no active sending of data to the usb device. <code>UsbOnDataReceived</code> on the other hand is actually doing something with an buffer of 64 bytes (line: 528-531).</p>
<p>When data is received an internal <code>packet_counter</code> is increased. Each packet has a size of 64 bytes (line: 529). The packet is copied into the <code>inPacket</code> array, and the <code>dataReceived</code> variable is set to true.</p>
<p>My guess is, that somewhere, something, somehow, might be, is waiting for a packet to arrive and waits until <code>dataReceived</code> is true. In <em>dotPeek</em> we can use the magic function &ldquo;Find Usages&rdquo; again, to find out more.</p>
<p><img src="https://dl.goatpr0n.events/$/HdRfX" alt=""></p>
<h3 id="prototyping-and-understanding-the-program">Prototyping and understanding the program</h3>
<p>Remember the <code>timer2</code> instance mentioned before? No, try to find the hint on <a href="https://goatpr0n.farm">this page</a>.</p>
<pre><code>// Formload.cs:1219
private void timer2_Tick(object sender, EventArgs e)
{
byte num1 = 0;
if (!this.dataReceived)
return;
this.dataReceived = false;
if ((int) this.inPacket[1] == 240)
{
if (this.bwrite_chrager_data)
return;
int num2 = (int) MessageBox.Show(&quot;OK!&quot;);
}
else if ((int) this.inPacket[1] == 95)
this.Get_Charge_Data((int) this.inPacket[2]);
else if ((int) this.inPacket[1] == 90)
{
this.Get_System_Data((int) this.inPacket[3]);
}
else
{
if ((int) this.inPacket[1] != 85)
return;
for (int index = 1; index &lt; 64; ++index)
num1 += this.inPacket[index];
if ((int) num1 != (int) this.inPacket[64])
return;
</code></pre>
<p>The <code>timer2</code> will process incoming data send from the device to the connected computer. The code begins with comparing index 1 of the <code>inPacket</code> with a series of values.</p>
<p>By looking at the code we might be able to assume we are looking at the first bytes necessary to communicate with the device. Here are some guesses:</p>
<pre><code>Value Description
240 (0xf0) Confirmation sent by charger.
95 (0x5f) Get charger data by information provided in index 2,
which is an number \[4\].
90 (0x5a) Get system data by information provided in index 3,
which is a number.
85 (0x55) Do not process the packet here. Otherwise calculate
the sum of all bytes in `num1` and compare it to the
information stored in index 64.
</code></pre>
<p>If these checks do not result in an premature <code>return</code>, the values from <code>inPacket</code> are copied into variables. Some variable names are recognizable and help in our guessing game to find out what this function does.</p>
<p><img src="https://dl.goatpr0n.events/$/nRAoH" alt=""></p>
<p>With the looks of it we are reading battery information. As an example on how the packet is decoded, we will have a look at the following code:</p>
<pre><code>this.Current[j] = (int) this.inPacket[11] * 256 + (int) this.inPacket[12];
</code></pre>
<p>The contents of <code>inPacket</code> index 11 and 12 is assigned the the variable <code>Current</code> at index <code>j</code>. Which is irrelevant at this point. But we need to understand what is happing with this multiplication and addition.</p>
<p>The multiplication by 256 is just a different way to express a left shift by 8. What happens, when we take the value 1 and multiply it by 256 or do a left shift by 8? In binary representation it will become very easy to understand.</p>
<pre><code> 1 =&gt; 0b1
256 =&gt; 0b100000000
</code></pre>
<p>So what if we take 256 times 1?</p>
<pre><code> 256 =&gt; 0b100000000
</code></pre>
<p>And if we take the value <code>0b1</code> and move the <code>1</code> exactly 8 positions to the left, like a left shift, <em>duh</em>?</p>
<pre><code> 1 &lt;&lt; 0 = 0b1
1 &lt;&lt; 1 = 0b10
1 &lt;&lt; 2 = 0b100
1 &lt;&lt; 3 = 0b1000
1 &lt;&lt; 4 = 0b10000
1 &lt;&lt; 5 = 0b100000
1 &lt;&lt; 6 = 0b1000000
1 &lt;&lt; 7 = 0b10000000
1 &lt;&lt; 8 = 0b100000000
</code></pre>
<p>This is just a step by step illustration. Computer do fast. Computer do in single step.</p>
<p>After the left shift, the second value is added to the variable. In other words, we are reading two bytes and concatenate it to have a word (2 bytes).</p>
<p>The same applies to the other functions <code>Get_Charge_Data</code> and <code>Get_System_Data</code> where the <code>inPacket</code> is read.</p>
<p>But how am I supposed to create my own library with this?</p>
<p><img src="https://dl.goatpr0n.events/$/ga4cq" alt=""></p>
<p>This is the part, where you take your favorite language, or a language good for prototyping and begin coding. First challenge would be to connect to the USB device. I am using the <code>pyusb</code> module with <em>Python</em>.</p>
<p>To connect an USB device we want to make sure we are using the right one. To do so, the USB devices can be identified by different properties, and one of them is the vendor and product id. The source of the program might give us enough information we need, as it needs to connect to the charger as well.</p>
<p>The product and vendor id can be found in the function <code>private void usbSendConnectData()</code> and is defined as:</p>
<pre><code>Field Value
Vendor ID 0
Product ID 1
</code></pre>
<h4 id="reading-data">Reading Data</h4>
<p>The people writing the firmware for the charger, did not care to give it some nice values, on the other hand 0 and 1 are nice. With these identifiers, it is possible to connect to our charger.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"> <span style="color:#f92672">import</span> usb
usb<span style="color:#f92672">.</span>core<span style="color:#f92672">.</span>find(idVendor<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>, idProduct<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>)
</code></pre></div><p>This will return a list of devices, even when the list is empty or contains just one element. Set up the USB device further and start building your first packet to send. Before blindly sending commands to the charger, what would be the most destructive - errr - non destructive command: getting device information.</p>
<p>Do some reads on the device without sending anything to it. Eventually you will receive a packet.</p>
<p>In this scenario the packets have a common format for receiving and sending. You might notice a <code>0x0f</code> at the beginning of each packet. As <em>dotPeek</em> is unable to tell you where it comes from and where it is designed, I am going to spoil it for you.</p>
<p>In file <code>FormLoader.cs</code> in line 201 we find the following definition:</p>
<pre><code>public const byte UART_DATA_START = 15;
</code></pre>
<p>The UART [6] we are basically telling the charger we are coming over USB. There is also a mobile application to send commands via Bluetooth, but I haven&rsquo;t done this one, yet.</p>
<p>There is function called <code>Get_System_Data</code>. When we look at the definition of the function the code is very messy.</p>
<p><img src="https://dl.goatpr0n.events/$/JP7Lc" alt=""></p>
<p>Alot [5]_ of constant values are assigned to variables, which are assigned to variables and then used as index. This looks confusing but the best way is to just begin prototyping it the same way.</p>
<pre><code>num1 = 0
str1 = ''
num2 = 4
# Do not kown this yet
inPacket = raw_packet # raw_packet is the contents read by pyusb.
index1 = num2
num3 = 1
num4 = index1 + num3
num5 = int(inPacket[index1], 16) # Python 3: probably bytes as input.
...
</code></pre>
<p>And so on. After building your prototyped function you will see parts which can be optimized, but do not care about it too much in the beginning. Try to understand how packets are constructed and what they contain. But for example the <code>num2 = 4</code> could be removed and replaced with <code>index1 = 4</code>, as <code>num2</code> is not used after that point.</p>
<p>By breaking the packet down, byte by byte (there are only 64 bytes), we then try to create data structures from it, like the one mentioned in the beginning. Each information gathered so far helps in decoding packets received and to later send packets.</p>
<p>For decoding packets I personally use the <code>[Python struct](https://docs.python.org/3/library/struct.html)</code> module. By reading the definition of <code>Get_System_Data</code> we define a system class, and <code>machine_info</code> as <code>FormLoader.cs</code> calls it.</p>
<p>With <code>struct</code> we define a data structure which can parse the 64 bytes each packet has and apply it to a named tuple in <em>Python</em>. After reading the original decompiled code, I came up with this definition:</p>
<pre><code>#: Machine response data
MACHINE_INFO_STRUCT = '&gt;3xBBBBBBBBBBBBB6sBBhBBBBbB'
#: Tuple for device information
MachineInfo = namedtuple('machine_info',
['Sys_Mem1', 'Sys_Mem2', 'Sys_Mem3', 'Sys_Mem4',
'Sys_Advance', 'Sys_tUnit', 'Sys_Buzzer_Tone',
'Sys_Life_Hide', 'Sys_LiHv_Hide',
'Sys_Eneloop_Hide', 'Sys_NiZn_Hide', 'Sys_Lcd_time',
'Sys_Min_Input', 'core_type', 'upgrade_type',
'is_encrypted', 'customer_id', 'language_id',
'software_version_hi', 'software_version_lo',
'hardware_version', 'reserved', 'checksum',
'software_version', 'machine_id'])
</code></pre>
<p>The struct definition <code>MACHINE_INFO_STRUCT</code> describes how each byte of the packet should be interpreted. In words:</p>
<ul>
<li>We decode it as big-endian.</li>
<li>Ignore 3 bytes as these are protocol commands.</li>
<li>Read 14 unsigned bytes (0..255), each into a separate variable.</li>
<li>Read 6 characters or a string of length 6.</li>
<li>Read 2 individual bytes.</li>
<li>Read a short (2 bytes).</li>
<li>Read 4 individual unsigned bytes.</li>
<li>Read a signed byte (-128..127).</li>
<li>Read a unsigned byte.</li>
</ul>
<p>The <code>MachineInfo</code> is a <code>[namedtuple](https://docs.python.org/3/library/collections.html#collections.namedtuple)</code>, to make it very easy to assign and access values. When we receive a packet and we have determined the type, we can do something like this:</p>
<pre><code>data = unpack(MACHINE_INFO_STRUCT, response[:32])
machine = MachineInfo(\*data, 0, machine_id)
</code></pre>
<h4 id="sending-data">Sending Data</h4>
<p>While reading data is one side, we also need send commands. When optimizing the code the <code>private bool Send_USB_CMD(int Solt, byte CMD)</code> function can be annoying, but refactoring the prototype code will very quickly tell you where to place your bytes.</p>
<p>Whilst the original code is hiding the <code>CMD</code> parameter position behind some index calculations (which lies in nature of decompilation) we can translate the following code:</p>
<p><img src="https://dl.goatpr0n.events/$/JEIdW" alt=""></p>
<p>To a single byte-string if we use the <code>Get_System_Data</code> CMD code 95:</p>
<pre><code>\x0f\x00\x5a\x00
</code></pre>
<p>One really annoying thing is the index counting. The program starts filling the <code>outPacket</code> at offset 1. Which is actually 0, which is always set to <code>0x0f</code>. It is protocol definition.</p>
<p>Tricky thing is the real offset 1. It has to be set to a specific value. To find out which one, we have to further investigate the code. This changes depending on the operation you want to call.</p>
<p><img src="https://dl.goatpr0n.events/$/JYRNp" alt=""></p>
<p>Going further through the code, we might find a location where it sets the offset 1 to a other value than 0. Eventually the offset becomes 4. The command so far is now:</p>
<pre><code>\x0f\x03\x5a\x00
</code></pre>
<p>Sending this to the device returns no result, therefore we are still missing something. Somewhere was a loop adding up all bytes of packet. This could be a checksum and/or the command is still incomplete. Let&rsquo;s look at the <code>Send_USB_CMD</code> again. When working through the code, it is help full to take notes.</p>
<p><em>I have removed a switch-case statement for your convenience.</em></p>
<p><img src="https://dl.goatpr0n.events/$/tkE9j" alt=""></p>
<p>After working through the code, taking notes. the resulting packet for <code>CMD=95, Solt=0</code> [7] should look like this:</p>
<pre><code>\x00\x0f\x03\x5a\x00\x00\x5d\xff\xff
</code></pre>
<p>The two bytes of <code>\xff</code> (255) at the end define the end a packet. Every packet is produced after this schema.</p>
<pre><code>Byte Description
1 It is always 0, you will learn soon enough why! _ARRGHGGHG_
2 Start of message (Always 0x0f (15)).
3 Calculate the value based on the index.
4 The command op code.
5 It is 0.
6 The slot index (0 to 3 (4 Slots)).
7 The sum of the variable data (Byte 3 to 6)
8 Is always 0xff (255)
9 Is always 0xff (255)
</code></pre>
<p>Sending this to the device is still not correct, why? To find out why, delving deeper into the nested classes we find an abomination. The decompiled code for SpecifiedOutputReport.cs in <code>class UsbLibrary.SpecifiedOutputReport</code>, there is this one function:</p>
<pre><code>public bool SendData(byte[] data)
{
byte[] buffer = this.Buffer;
for (int index = 1; index &lt; buffer.Length; ++index)
buffer[index] = data[index];
return true;
}
</code></pre>
<p>The line 19 defines a loop starting at index 1…</p>
<p><img src="https://dl.goatpr0n.events/$/N97jA" alt=""></p>
<p>With all this knowledge collected the final valid packet to send to your device is:</p>
<pre><code>\x0f\x03\x5a\x00\x00\x5d\xff\xff
</code></pre>
<p>That&rsquo;s it. We have done it.</p>
<p>KTHXBYE!</p>
<ol>
<li>BTW, giving descriptive names for your variables is totally over rated.</li>
<li>Bob, is it you? [3]</li>
<li>Stupidest joke so far. He is no constructor, he is a builder.</li>
<li>As you might have noticed. I am just reading and translating the code.</li>
<li>|alot| this was an intentional typo.</li>
<li>Universal Asynchronous Receiver/Transmitter</li>
<li>Solt <em>[SIC]</em></li>
</ol>
<h4><a href="https://goatpr0n.farm/">Back to Home</a></h4>
</div>
</div><footer class="container">
<hr class="soften">
<p>
&copy;
Julian Knauer
<span id="thisyear">2020</span>
</p>
<p class="text-center">
</p>
</footer>
<script src="/js/jquery.js"></script>
<script src="/js/bootstrap-386.js"></script>
<script src="/js/bootstrap-transition.js"></script>
<script src="/js/bootstrap-alert.js"></script>
<script src="/js/bootstrap-modal.js"></script>
<script src="/js/bootstrap-dropdown.js"></script>
<script src="/js/bootstrap-scrollspy.js"></script>
<script src="/js/bootstrap-tab.js"></script>
<script src="/js/bootstrap-tooltip.js"></script>
<script src="/js/bootstrap-popover.js"></script>
<script src="/js/bootstrap-button.js"></script>
<script src="/js/bootstrap-collapse.js"></script>
<script src="/js/bootstrap-carousel.js"></script>
<script src="/js/bootstrap-typeahead.js"></script>
<script src="/js/bootstrap-affix.js"></script>
<script>
_386 = {
fastLoad: false ,
onePass: false ,
speedFactor: 1
};
function ThisYear() {
document.getElementById('thisyear').innerHTML = new Date().getFullYear();
};
</script></body>
</html>