<?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: Dizzyspiral</title>
    <description>The latest articles on DEV Community by Dizzyspiral (@dizzyspi).</description>
    <link>https://dev.to/dizzyspi</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%2F332874%2F280637bf-ff2d-4c91-80bb-f3cfe172e7b6.jpeg</url>
      <title>DEV Community: Dizzyspiral</title>
      <link>https://dev.to/dizzyspi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dizzyspi"/>
    <language>en</language>
    <item>
      <title>"Beating" Checksums</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Wed, 26 Oct 2022 23:49:06 +0000</pubDate>
      <link>https://dev.to/dizzyspi/beating-checksums-p7e</link>
      <guid>https://dev.to/dizzyspi/beating-checksums-p7e</guid>
      <description>&lt;p&gt;Checksums. The often-ignored string of hex characters that accompany most software downloads. Checksums are meant to help you, the end user downloading and installing software from the wild wild internet, verify that what you got is actually what the developers released.&lt;/p&gt;

&lt;p&gt;Being the very responsible and security-conscious person you are, I'm sure you verify the checksum on everything you download. Do you actually &lt;code&gt;diff&lt;/code&gt; the checksums? Or do you, like me, just eyeball it sometimes?&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%2Fcu8mzyh3p3ng25dqkfei.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%2Fcu8mzyh3p3ng25dqkfei.png" alt="TrueNAS Core sha256 checksum"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I bet you eyeball it. It's faster. We're lazy. And checksums are designed to be wildly different with just small changes in the input. So if, say, the beginning and the end of the checksums match, chances are the whole thing matches. Right?&lt;/p&gt;

&lt;p&gt;Right?&lt;/p&gt;

&lt;p&gt;That's the subject of today's blog post! Beating checksums is a thing security researchers often have to, though typically they're not packaging their stuff to &lt;em&gt;be&lt;/em&gt; a particular checksum if they can help it. That's hard to do, and usually it's easier to find a way to defeat the check - like stuffing your code/data/whatever into something that doesn't get checksum'd at all. But what if you &lt;em&gt;have&lt;/em&gt; to beat the checksum? And what if the checksum you're beating is checked by a human?&lt;/p&gt;

&lt;p&gt;I hypothesize (from my very rigorous sample size of me and my coworkers) that lots of people only check the beginning and end of a checksum, and maybe a few stand-out patterns in the middle if they're feeling particularly paranoid. Can we build something that'll pack arbitrary data to match the beginning and end of a checksum? Let's call the beginning/end about 5 characters each, in keeping with the researched &lt;a href="https://en.wikipedia.org/wiki/Short-term_memory" rel="noopener noreferrer"&gt;limits of short-term memory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I know nothing about checksums, and today isn't the day I'm going to get all hot and bothered for math, so I'm going to do a little light research on manipulating checksums and see what techniques are generally used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research
&lt;/h2&gt;

&lt;p&gt;Okay so first off, I found &lt;a href="https://link.springer.com/chapter/10.1007/978-3-642-00205-2_1" rel="noopener noreferrer"&gt;this paper&lt;/a&gt; that explained something I knew to be true - CRC checksums aren't secure (and are largely not used anymore because of this). I mostly see sha256 these days, so that's what we'll try to "beat."&lt;/p&gt;

&lt;p&gt;Second... it turned out there's no way to get a handle on this problem without learning &lt;em&gt;some&lt;/em&gt; math (ugh) so here we go on SHA-256:&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%2F7ldo2k4oq0zuxxru6che.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%2F7ldo2k4oq0zuxxru6che.png" alt="A graphic depicting the sha256 algorithm internals"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://commons.wikimedia.org/w/index.php?curid=1823488" rel="noopener noreferrer"&gt;By kockmeyer - Own work, CC BY-SA 3.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SHA-256 is a hashing algorithm that turns your input into 512-bit chunks and does some math on them, then uses that to produce a 256-bit value. This hash is computed 64 times, with new input data for each round based on the previous round. If you want a deep dive into the math on this, here's &lt;a href="https://www.youtube.com/watch?v=y3dqhixzGVo&amp;amp;ab_channel=KenShirriff" rel="noopener noreferrer"&gt;a video of doing a round out by hand&lt;/a&gt;. If you want something a bit more digestible, &lt;a href="https://www.youtube.com/watch?v=f9EbD6iY9zI&amp;amp;ab_channel=learnmeabitcoin" rel="noopener noreferrer"&gt;this video is an excellent visual breakdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Third, there's lots of bitcoin bullshit videos out there saying that hashes are always unique. This isn't true. 256 bits is a lot of playground to work with -  115,​792,​089,​237,​316,​195,​423,​570,​985,​008,​687,​907,​853,​269,​984,​665,​640,​564,​039,​457,​584,​007,​913,​129,​639,​935 unique values, to be exact. And while conceptually this means you could have a unique hash for every bit of unique data that could ever be created, things are just not quite that simple. Hash collisions (where the same hash is generated for two completely different inputs) exist. They're just hard to find.&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%2F16hhqo9iqniddp7epd57.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%2F16hhqo9iqniddp7epd57.png" alt="A depiction of the bitcoin mining workflow, where transactions are sent to miners to calculate the correct hash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://en.bitcoinwiki.org/index.php?curid=57307" rel="noopener noreferrer"&gt;Credit&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fourth, hey look bitcoin miners are basically already doing this. I knew that, but I guess I forgot. Proof-of-work crypto schemes take a transaction and append a nonce to them in order to achieve a particular prefix on the resultant sha256 hash of transaction + nonce. This seems like a great place to start this research.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Okay so what we want to do is manipulate arbitrary data so that the first and last five characters of its sha256-sum match an arbitrary sha256-sum. We think we can take the same thing crypto miners are doing - fuzzing an appended nonce - to find a hash that has a given prefix and suffix.&lt;/p&gt;

&lt;p&gt;While ostensibly I could take some code already written for this by the crypto bros, as a practical matter that's all going to have coin-specific code and blockchain network blah blah in it, and anyway I didn't find anything suitable in my 10 seconds of looking.&lt;/p&gt;

&lt;p&gt;Instead, I'm going to try rolling my own. I'm itching to code - let's go!&lt;/p&gt;
&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;This problem is actually easy enough that we could probably get a prototype going using bash. But since I'm confident from my research that the approach is going to work, and I'm really not a fan of debugging bash, I'm going to throw this together in python.&lt;/p&gt;

&lt;p&gt;Quick aside, python is really not the right choice for a problem like this, since it's an obviously intense and parallelizable problem, and &lt;a href="https://realpython.com/python-gil/" rel="noopener noreferrer"&gt;python is shit at parallel processing&lt;/a&gt;. However, in the long run I want to experiment with this by modifying known data types (e.g. ELF, PDF) in non-destructive ways other than just appending a value. Python has a lot of great tools to do that quickly, so that's why python.&lt;/p&gt;

&lt;p&gt;Here's our initial implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;

&lt;span class="n"&gt;NONCE_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# 1 byte
&lt;/span&gt;&lt;span class="n"&gt;MAX_NONCE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NONCE_SIZE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SHA256 prefix and suffix mutator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metavar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file to hash&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--prefix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prefix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prefix to match&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--suffix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suffix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suffix to match&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Initial SHA256 digest: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MAX_NONCE = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MAX_NONCE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="c1"&gt;# Precompute for speed
&lt;/span&gt;    &lt;span class="n"&gt;prefix_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;suffix_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nf"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;prefix_len&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;suffix_len&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;MAX_NONCE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NONCE_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;big&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got hash &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; using nonce &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that, we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py main.py --prefix a --suffix b
Initial SHA256 digest: 35064e6ca1abd7b5f51f353e5d558aa947e389480ca6bdadc5660a32e615f88d
MAX_NONCE = 255
Traceback (most recent call last):
  File "main.py", line 47, in &amp;lt;module&amp;gt;
    mutate(data, args.prefix, args.suffix)
  File "main.py", line 32, in mutate
    message.update(nonce.to_bytes(NONCE_SIZE, "big"))
OverflowError: int too big to convert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we up our nonce size:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py main.py --prefix a --suffix b
Initial SHA256 digest: 9482419d29e5a2579d7ce71373d3aa5fc4a12a45a2378629c8e34bdd95174015
MAX_NONCE = 4294967295
Got hash aca28c372935e0b10acacbb81bf3b427ca5d64ae91b80870be3d71a4b0a9023b using nonce 28
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll note that this modified code only took 28 tries, which is less than 255 - because we're running this with our code as input (because lazy), this isn't very deterministic or repeatable as we iterate on the code.&lt;/p&gt;

&lt;p&gt;Let's use some static sample data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's add some profiling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cProfile&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mutate(data, args.prefix, args.suffix)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py lorem.txt --prefix a --suffix b
Initial SHA256 digest: 56293a80e0394d252e995f2debccea8223e4b5b2b150bee212729b3b39ac4d46
MAX_NONCE = 4294967295
Got hash ae7ee87838542a984c3b82c9884f75231c870776500d6dfed3777566c5cdf07b using nonce 269
         1362 function calls in 0.001 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 &amp;lt;string&amp;gt;:1(&amp;lt;module&amp;gt;)
        1    0.000    0.000    0.001    0.001 main.py:16(mutate)
      271    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_sha256}
        1    0.000    0.000    0.001    0.001 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
      271    0.000    0.000    0.000    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
      270    0.000    0.000    0.000    0.000 {method 'to_bytes' of 'int' objects}
      541    0.000    0.000    0.000    0.000 {method 'update' of '_hashlib.HASH' objects}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The profiling will be nice when we start upping the prefix and suffix lengths... as we'll do now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py lorem.txt --prefix aa --suffix bb
Initial SHA256 digest: 56293a80e0394d252e995f2debccea8223e4b5b2b150bee212729b3b39ac4d46
MAX_NONCE = 4294967295
Got hash aa08146063c11f7b153146027543b0d0de02207048ce024d074b6010f86d6fbb using nonce 111950
         559767 function calls in 0.258 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.258    0.258 &amp;lt;string&amp;gt;:1(&amp;lt;module&amp;gt;)
        1    0.076    0.076    0.258    0.258 main.py:16(mutate)
   111952    0.013    0.000    0.013    0.000 {built-in method _hashlib.openssl_sha256}
        1    0.000    0.000    0.258    0.258 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
   111952    0.039    0.000    0.039    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
   111951    0.013    0.000    0.013    0.000 {method 'to_bytes' of 'int' objects}
   223903    0.117    0.000    0.117    0.000 {method 'update' of '_hashlib.HASH' objects}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding two more bytes (one to the prefix, one to the suffix) has drastically increased our running time. Add two more, and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py lorem.txt --prefix aaa --suffix bbb
Initial SHA256 digest: 56293a80e0394d252e995f2debccea8223e4b5b2b150bee212729b3b39ac4d46
MAX_NONCE = 4294967295
Got hash aaaace2fd053c08a86ed9aa2d6b9bb4cd2ee98935f197fc9f28c7020fe6debbb using nonce 5950739
         29753712 function calls in 13.329 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   13.329   13.329 &amp;lt;string&amp;gt;:1(&amp;lt;module&amp;gt;)
        1    3.800    3.800   13.329   13.329 main.py:16(mutate)
  5950741    0.648    0.000    0.648    0.000 {built-in method _hashlib.openssl_sha256}
        1    0.000    0.000   13.329   13.329 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  5950741    2.035    0.000    2.035    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
  5950740    0.734    0.000    0.734    0.000 {method 'to_bytes' of 'int' objects}
 11901481    6.112    0.000    6.112    0.000 {method 'update' of '_hashlib.HASH' objects}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is &lt;em&gt;very&lt;/em&gt; obvious where the time is spent. Let's graph those data points, for science.&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%2Fhfxt4vvhyukre8yr73i9.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%2Fhfxt4vvhyukre8yr73i9.png" alt="A graph of running times for our script, which increases exponentially with the number of bytes in the prefix and suffix"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to get it to match 5 characters (so 5 bytes) from the prefix and suffix each, so we're really aiming to mutate 10 bytes. Initial findings are... not good, on the runtime scale. Which is not a surprise.&lt;/p&gt;

&lt;p&gt;We can get some extra performance by multi-threading, but as mentioned earlier, python is not the best language for this. Still, let's give it a go and see how we do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cProfile&lt;/span&gt;

&lt;span class="n"&gt;NONCE_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;  &lt;span class="c1"&gt;# 4 bytes
&lt;/span&gt;&lt;span class="n"&gt;MAX_NONCE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NONCE_SIZE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# Largest int you can make with the number of nonce bytes
&lt;/span&gt;&lt;span class="n"&gt;NUM_THREADS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;

&lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SHA256 prefix and suffix mutator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metavar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file to hash&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--prefix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prefix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prefix to match&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--suffix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suffix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;suffix to match&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--multithread&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;multithread&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;store_const&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enable multithreading&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nonces&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Precompute for speed
&lt;/span&gt;    &lt;span class="n"&gt;prefix_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;suffix_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nonces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If another thread got the answer, quit
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NONCE_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;little&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Change to little, because intel
&lt;/span&gt;        &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# If we got the anwswer, set the done flag and quit
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;prefix_len&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;suffix_len&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got hash &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; using nonce &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NUM_THREADS&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;start_nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_NONCE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;NUM_THREADS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;end_nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;MAX_NONCE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;NUM_THREADS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting thread with nonce range &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;start_nonce&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end_nonce&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_nonce&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
        &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;multithread&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;start_threads(data, args.prefix, args.suffix)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mutate(data, args.prefix, args.suffix, range(MAX_NONCE))&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I changed the byte ordering to little endian, and we changed some other stuff about how the algorithm works, let's re-benchmark our single-threaded execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py lorem.txt --prefix aaa --suffix bbb
Got hash aaa07ea3b6faf37e8ad580ad7b60bf5b58fa1d30cf898c22819fff562261abbb using nonce 35489965
         177449839 function calls in 77.409 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, multithreaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py lorem.txt --prefix aaa --suffix bbb --multithread
Starting thread with nonce range 0 to 536870912
Starting thread with nonce range 536870911 to 1073741824
Starting thread with nonce range 1073741822 to 1610612736
Starting thread with nonce range 1610612733 to 2147483648
Starting thread with nonce range 2147483644 to 2684354560
Starting thread with nonce range 2684354555 to 3221225472
Starting thread with nonce range 3221225466 to 3758096384
Starting thread with nonce range 3758096377 to 4294967295
Got hash aaae5c17d3d40008c490879d1d227c63dda58d9fc4e9244025965f191df5fbbb using nonce 1076940590
         356 function calls in 36.090 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we've cut execution time in about half, based on this one datapoint. I guess let's let it rip on just one more byte, in the prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─[dizzyspiral@akatsuki:~/repos/github/sha-bam]
└─╼ python3 main.py lorem.txt --prefix aaaa --suffix bbb --multithread
Starting thread with nonce range 0 to 536870912
Starting thread with nonce range 536870911 to 1073741824
Starting thread with nonce range 1073741822 to 1610612736
Starting thread with nonce range 1610612733 to 2147483648
Starting thread with nonce range 2147483644 to 2684354560
Starting thread with nonce range 2684354555 to 3221225472
Starting thread with nonce range 3221225466 to 3758096384
Starting thread with nonce range 3758096377 to 4294967295
^C         274 function calls in 534.320 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It ran for a while, but I got impatient and killed it.&lt;/p&gt;

&lt;p&gt;So now we're faced with a fundamental question. Do we throw more compute at this, or do we try to do something clever to reduce the compute required? The former option isn't very interesting; crypto-mining has shown us that we can solve this problem with beefy GPUs. But that's not super attainable for the average dudette. Is there a way we can give ourselves a better chance of affecting the prefix and suffix of the hash?&lt;/p&gt;

&lt;p&gt;First thing that comes to mind is changing up where we're inserting our nonce. If we put it at the beginning of the data, is it more likely to affect the prefix? We can do some benchmarking on randomly generated data to find out.&lt;/p&gt;

&lt;p&gt;For what it's worth, I do doubt that will make a difference, because it would indicate a flaw in the checksum that could allow you to learn something about the original data. And sha256 has been in use for so long that such a trivial attack would be well known by now if it existed. But I take nothing for granted, so I'll definitely try it. In the next blog post ;)&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>research</category>
      <category>programming</category>
    </item>
    <item>
      <title>Reversing the asciiPad SG-6 serial link (part 2)</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 24 Jul 2022 23:42:45 +0000</pubDate>
      <link>https://dev.to/dizzyspi/reversing-the-asciipad-sg-6-serial-link-part-2-28dc</link>
      <guid>https://dev.to/dizzyspi/reversing-the-asciipad-sg-6-serial-link-part-2-28dc</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a writeup I've moved here from my personal website. It was originally published on 4/13/19&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So remember how I said you could probably just hook your controller up to a bench power supply? Yeah, don’t do that. It’s a no-go. Apparently the Sega controller receives a clock signal from the Sega that drives some of the pin output. The controllers for the Sega Genesis don’t behave at all like the pinout I showed you yesterday. This is what a good trace of a stock Sega Genesis controller looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kS0ySs6n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0ugiq24x0mqok8a9wl1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kS0ySs6n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0ugiq24x0mqok8a9wl1w.png" alt="logic-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me explain how I learned this.&lt;/p&gt;

&lt;p&gt;This morning I woke up energized and ready to go (masala chai tea for the win!), so I tackled the issue of getting the Sega to actually output video to a TV. While I was twiddling the video cable I learned something exciting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TZ3fb1qj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4w69bssw5d9416acbt7x.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TZ3fb1qj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4w69bssw5d9416acbt7x.jpeg" alt="sega-loose-connector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My Sega’s video output now comes in two pieces! Lovely. I futzed with this a bit until it fit snugly back into its housing. This resulted in a sort of okay video output to the TV. I could at least see that the game was booting up and enjoy visual feedback when I pressed controller buttons. I decided it was time to eliminate some variables and go back to the baseline — I hooked up the stock controller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pJWuQvGN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qicc2cq9hhxeuri0kiqm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pJWuQvGN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qicc2cq9hhxeuri0kiqm.png" alt="logic-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r__sroA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vx9df7tim1u8ozhd1you.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r__sroA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vx9df7tim1u8ozhd1you.png" alt="logic-3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without pressing any buttons, we get three lines with an even clock signal. This was way different from what I saw yesterday messing with the asciiPad. It could be a difference in the controller, but it could also be a result of having a game loaded. I rebooted the Sega and took a trace of it at startup to see when the clocked signals appeared. If they appeared as soon as the controller got power, it would be likely that the controller was generating the clock and sending it to the Sega. But if there was a delay between when the lines went high and when they gained a clock, it was more likely that the clock was a result of the game being loaded, and therefore generated by the Sega and sent to the controller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nYkN6y3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pdag9lsj3sfl7pb2n8o2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nYkN6y3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pdag9lsj3sfl7pb2n8o2.png" alt="logic-4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sure enough, we see the latter case. For further sanity checking and information gathering, I replaced the OEM Sega controller with the asciiPad after bootup and took a trace while pressing buttons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jDibHiFV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ae9xbkkwmxo96aq9i4rp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jDibHiFV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ae9xbkkwmxo96aq9i4rp.png" alt="logic-5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a logic trace with clock, and a number of button presses in order of: up, down, left right, z, c, x, y, a, b, start, mode&lt;/p&gt;

&lt;p&gt;We still see the clocked signals, which continues to support the idea that they’re coming from the Sega. But more than that, check out what happens when you press buttons! With the clock, we can actually see many of the buttons that were missing yesterday. You’ll note that the standard buttons exactly match the trace with the stock controller, but we get one additional button here that we didn’t have there — we have Z, which apparently drives all of the clock lines (and possibly just all of the lines in general) high. This is interesting because without a clock signal, other button presses like Start should not be possible.&lt;/p&gt;

&lt;p&gt;X and Y are still missing from the trace. It doesn’t make sense to me that the AsciiWare folks would put non-functional buttons on the controller unless they used this controller interface for other systems too. In that case, it’d be a cost savings for them to only manufacture one controller and program the chip inside with whatever logic it needed to interface with the game system it’s marketed for. I could do some research to try and figure this out, but I had another theory I wanted to poke at first.&lt;/p&gt;

&lt;p&gt;Recall the trace of the lines at bootup. There’s a little blip at the beginning that’s separated from the rest of the uniform clock signal. This could be the Sega telling the controller “hi, I’m here.” It could just be noise. But it’s definitely different from the rest of the trace. I zoomed in on it, and saw something weird.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y0M28OOA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6hlz13vvxd5ak3e27r5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y0M28OOA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6hlz13vvxd5ak3e27r5o.png" alt="logic-6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The clock signal after this “blip” has pairs of short pulses instead of the usual single pulse. I thought maybe it was an anomaly in the reading, but when I took another, I saw the same thing. The double pulses continue for quite a while, about 8 seconds, before they switch to the single pulse.&lt;/p&gt;

&lt;p&gt;This got me thinking, do different games have different bootup behavior with respect to the clock? I’ve got a plethora of old Sega games, so I went to town plugging and unplugging them, taking traces of their behavior at bootup (only cursing occasionally as I blew into cartridges and enjoyed all of that old-timey dust-and-electronics smell). Here are a few traces just for your viewing pleasure.&lt;/p&gt;

&lt;p&gt;A Sonic 2 bootup trace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZASDzlp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/17tvnc4zdvsgpob3s3yu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZASDzlp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/17tvnc4zdvsgpob3s3yu.png" alt="logic-7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NFL 95 bootup trace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iZv-bVBE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qv5qoeuadjqe7ct8c3ry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iZv-bVBE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qv5qoeuadjqe7ct8c3ry.png" alt="logic-8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lion King bootup trace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0IBJLNex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q0nf7guled8wh01igzm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0IBJLNex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q0nf7guled8wh01igzm9.png" alt="logic-9"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NHL Hockey bootup trace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ssx6hquN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ovzkphrf4m9o1xzklkwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ssx6hquN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ovzkphrf4m9o1xzklkwp.png" alt="logic-10"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond each game having their own unique clock bootup shenanigans, they also clock different lines, and clock them inconsistently. Check out this zoomed-in view of the NFL 95 game. This is one clock “pulse” as it appears on several different lines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ePIblyAF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ghh4ffv2j18ryh5t3ocp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ePIblyAF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ghh4ffv2j18ryh5t3ocp.png" alt="logic-11"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since NFL 95 drove clock signals over more lines, I decided to see if I could find my missing buttons in them. I didn’t, but I did find a few other interesting things. First, when running NFL 95 it seems (under most circumstances) pressing a button related to a clocked line doesn’t remove the clock signal from the line. This is different from other games, where pressing a button makes a clocked line steady at 5v or ground. The exception to this is when you press Start. In this case, we see a single pulse on pin 9, and then all of the lines are drive high for about a second, including pin 9. The duration of the silence on the lines is not dependent on how long you hold the start button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Em9mlwFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ww9j8eki2d6eew1nnpzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Em9mlwFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ww9j8eki2d6eew1nnpzd.png" alt="logic-12"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--93r4z6ka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w2nedfncybettrh3k0x6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--93r4z6ka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w2nedfncybettrh3k0x6.png" alt="logic-13"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s hard to tell whether the game is intentionally removing the clock from the lines in order to prevent button presses from registering (if the controller doesn’t have a clock signal, it can’t send most button signals, as we saw yesterday) or if it’s a side effect of some processing going on in the Sega. Either way, it seems that the way the Sega handles the controller coms is completely dependent on the game. This is cool because it means a game developers can ship their own unique equipment to enhance a game if they want to, without being tied to the template provided by Sega — but it’s also annoying for anyone who wants to manufacture a controller for the game, because it means there is no guarantee that it will actually work with all games.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zO5yynzh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ig5hj3fhlalf78tdowzq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zO5yynzh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ig5hj3fhlalf78tdowzq.jpeg" alt="cartridge-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3IfjYXSW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vbybtkb6jr9y6cv1jkvw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3IfjYXSW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vbybtkb6jr9y6cv1jkvw.jpeg" alt="cartridge-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--woh-7o-F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yrhb843ud6629lrt2dki.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--woh-7o-F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yrhb843ud6629lrt2dki.jpeg" alt="cartridge-3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--miM7aFNP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sclyrjk0w9z0r47necce.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--miM7aFNP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sclyrjk0w9z0r47necce.jpeg" alt="cartridge-4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They’re about what I expected. There’s a package that is probably just storage that the CPU on the Sega reads in order to get the game’s program. The only thing that surprised me was that the game name is printed right on the chip — that suggests a custom run of these chips just for that cartridge.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://retroshock.bplaced.net/mdg.html"&gt;RetroShock&lt;/a&gt; project has a ton of information about individual cartridges, including which chips are used in which games, and the pinouts used by the different cartridges. The two photos I took are of cartridges that use the same pinout, but there are several different schemes, with some using all of the pins available on the 64-pin header and some using only a few.&lt;/p&gt;

</description>
      <category>reversing</category>
      <category>electronics</category>
    </item>
    <item>
      <title>Reversing the aciiPad SG-6 serial link (Part 1)</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 24 Jul 2022 23:41:00 +0000</pubDate>
      <link>https://dev.to/dizzyspi/reversing-the-aciipad-sg-6-serial-link-part-1-42o</link>
      <guid>https://dev.to/dizzyspi/reversing-the-aciipad-sg-6-serial-link-part-1-42o</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a writeup I've moved here from my personal website. It was originally published on 4/13/19&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The asciiPad SG-6 is an aftermarket Sega Genesis controller with extra buttons and macro features. As a kid, I had no idea what any of the weird switches did. I just sort of flicked them up and down until the controller worked as I expected. As an adult, I started wondering how the controller is able to support more buttons than the stock controller, let alone the frame rate slowing capability I remembered as a child. It’s a mystery I couldn’t just leave alone. To the batcave!&lt;/p&gt;

&lt;p&gt;I’m mostly just getting started as a hardware reverser, so my tools are nothing fancy. For this, I used a bunch of &lt;a href="https://www.amazon.com/HiLetgo-Breadboard-Prototype-Assortment-Raspberry/dp/B077X7MKHN/ref=sr_1_4?ie=UTF8&amp;amp;qid=1546720778&amp;amp;sr=8-4&amp;amp;keywords=jumper+wires"&gt;jumper wires&lt;/a&gt;, male and female &lt;a href="https://www.amazon.com/gp/product/B071ZLNDYT/ref=oh_aui_detailpage_o07_s00?ie=UTF8&amp;amp;psc=1"&gt;DB-9 breakouts&lt;/a&gt;, and a super budget &lt;a href="https://www.amazon.com/HiLetgo-Analyzer-Ferrite-Channel-Arduino/dp/B077LSG5P2/ref=sr_1_3?ie=UTF8&amp;amp;qid=1546720760&amp;amp;sr=8-3&amp;amp;keywords=logic+analyzer"&gt;logic analyzer&lt;/a&gt;. All told, materials cost around $20. Here’s the setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SuypFbea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5098rof0rhujp92sqxm5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SuypFbea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5098rof0rhujp92sqxm5.jpeg" alt="analysis-setup" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to capture the signals coming from the controller when different buttons were pressed and different modes were set. The controller is powered over the serial connection, though it uses a non-standard pinout. Ground is pin 8, VCC at 5v is on pin 5. The other pins float high at 4v9. I checked all of this with a multimeter (&lt;a href="https://www.amazon.com/Craftsman-34-82141-Digital-Multimeter-Functions/dp/B000X5TSUA/ref=sr_1_3?ie=UTF8&amp;amp;qid=1546720936&amp;amp;sr=8-3&amp;amp;keywords=craftsman+multimeter"&gt;mine is nothing fancy&lt;/a&gt;) before hooking anything up. Measure twice, cut once, as the saying goes.&lt;/p&gt;

&lt;p&gt;The controller should be completely standalone, so if you have a bench power supply, you can just set it to 5v and hook the + rail to pin 5 and the – rail to pin 8. I didn’t do this. Instead I opted to use a female DB-9 breakout connected to a male DB-9 breakout connected to the controller in order to power the controller and provide access to the pin signals. I did this because it’s cheap and it works. My little logic analyzer is connected to the male breakout with some jumper wires so it can read the pin logic levels. I collected traces for pins 1–4, 6, 7, and 9 (everything but the ground and VCC pin).&lt;/p&gt;

&lt;p&gt;From there, it was time to have some fun. I started with powering on the Sega and taking a trace to see if there were any bootup signals sent over the line by the controller or the Sega. There aren’t — the lines all just go high. Then I started taking traces with individual buttons. Which quickly became… odd.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wT0uNbKA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sxueq5fhvmw0o7o6k40h.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wT0uNbKA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sxueq5fhvmw0o7o6k40h.jpeg" alt="pinout" width="880" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For all of the pins, a logic level of 0 (the pin being high at 4v9) indicates that the button associated with that pin is not being pressed. A logic level of 1 (the pin being being driven to ground) indicates that the button is being pressed. The standard controller pinout generally associates one button with each pin, however there are a couple of cases where the pins are multiplexed and two buttons are capable of driving the pin. In the latter case, a special Select pin differentiates the two buttons by being either high or low. For example, the Start button and the C button are multiplexed on pin 9 — if the Select pin is low, and pin 9 is low, then this is interpreted by the Sega as the Start button being pressed. If the Select pin is high and pin 9 is low, it is interpreted as the C button being pressed.&lt;/p&gt;

&lt;p&gt;When I started pressing buttons on the asciiPad, only some of them affected the logic level on the pins. Really important, standard buttons, like A and Start, didn’t seem to have any effect on the pin outputs. But, you know, the asciiPad has a bunch of toggle switches and, who knows, some of them might be futzing up the operation of the buttons. It also has this nice “mode” button which, hell if I know what it does. It seemed like the next logical step was to hook the Sega up to a TV and get some intuition about what the odd buttons on the controller actually do, instead of relying on my decades old memories of how it worked from playing video games with my brother.&lt;/p&gt;

&lt;p&gt;While I kept all of the gear that came with the Sega, including the TV tuner, unfortunately the RF interface is and always has been super finicky. I wasn’t able to get it working in an amount of time that my patience would allow for. I did however take apart the Sega and attempt to diagnose any apparent connector issues (I didn’t find any). Here’s a few photos (note that this is not the “Mega Drive” described in &lt;a href="https://www.allaboutcircuits.com/news/retro-teardown-sega-genesis-sega-mega-drive-eu/"&gt;other teardowns&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d_1Wce9z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hmymuw3pq19vciujimds.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d_1Wce9z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hmymuw3pq19vciujimds.jpeg" alt="sega-pcb" width="880" height="1173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KWepjuww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zvmxev82qj3n8pyisamc.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KWepjuww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zvmxev82qj3n8pyisamc.jpeg" alt="sega-pcb2" width="880" height="1173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I went back to taking logic traces of the controller pins and, by some amount of trial and error, figured out what the “Auto/Turbo/Off” (ATO) switches on the controller do. First, visually it’s apparent that one switch is associated with each button on the controller other than the “Start” and “Mode” buttons. I switched each of ATO switches to off and captured the signals pictured below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MPI0d_rG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8uh568dkcn8qu8danh1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MPI0d_rG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8uh568dkcn8qu8danh1c.png" alt="logic-1" width="880" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order of activation, the buttons pressed during the trace were: Up, Down, Left, Right, Z, C, X, Y, A, B, Mode, Start. You’ll notice that we don’t have nearly enough pin activations in the trace to account for all of those buttons. What shows up on the trace are the D-pad presses and the B and C buttons, which correspond directly to the pinout described for the stock Sega controller. Clearly, something is up with the missing buttons. But we ignore that for now, and move on to setting all of the ATO switches to Turbo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CJKZwwMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vb6tb0n1wu5aj48qr0kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CJKZwwMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vb6tb0n1wu5aj48qr0kq.png" alt="logic-2" width="880" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turbo appears to quickly toggle a button as long as it is held down. This might be a great way to get in some quick punches in a fighting game, or float in a game with flying, or any number of other things.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P3qD8NWC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6rhn7hc0byo3zrofe9u9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P3qD8NWC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6rhn7hc0byo3zrofe9u9.png" alt="logic-3" width="880" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “Auto” mode of the asciiPad behaves like Turbo except it’s active as long as the button isn’t pressed. Once a button is pressed, the signal for that button is driven high, effectively telling the Sega that the button is currently not being pressed.&lt;/p&gt;

&lt;p&gt;The great mystery in all of these traces is why the X, Y, Z, A, Start, and Mode buttons don’t appear to affect the output on the pins at all. I could believe that Mode is perhaps a button that affects the controller operation and does not directly send anything out over the serial line. Similar arguments could be made for the other buttons, e.g. maybe they’re deactivated since a standard Sega controller only supports the A, B, and C buttons. But the A button is standard, and so is Start, and nowhere do we see either appearing in the output. Time to take the controller apart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QzYUsHOk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i3nljtw2qn3m3vhx1nn9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QzYUsHOk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/i3nljtw2qn3m3vhx1nn9.jpeg" alt="asciipad-internal-1" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the controller, the first thing that’s revealed is the chip that’s doing all of the signals processing for the controller. Beyond that, there really isn’t much to be seen — there’s some resistors and capacitors, but very few, and they’re probably just doing some stuff to the signals coming in from the buttons to the chip. Obviously there is also the header with the colorful wires coming out of it, which gets wrapped up into the black cord and becomes the serial connection to the Sega. The signals routed to that header come directly from the chip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D5puMIjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/42q5ji58erdf1nhuvtqv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D5puMIjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/42q5ji58erdf1nhuvtqv.jpeg" alt="asciipad-internal-2" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other side, we have the board components that interface with the buttons. For the pressure buttons, when they’re pressed they complete a circuit, which changes the signal that the chips on the other side of the board receives. For the toggle switches, they complete different circuits as they slide into one of three different positions. In the “Off” position, they complete no circuit; in the “Turbo” position they connect with the middle small grey piece, and in the “Auto” position they connect with the outside small grey piece. The exception to this is the “Fast/Slow” toggle, which only has two modes (this switch is located next to the Start and Mode buttons).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6kXG1l_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o92xmkvqxzo2v0bkx7pg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6kXG1l_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o92xmkvqxzo2v0bkx7pg.jpeg" alt="asciipad-internal-3" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When my patience returns, I’ll probably begin again by attempting to get the Sega hooked back up to a TV so I can functionally diagnose any issues with the controller. It’s possible that the buttons that don’t appear to send signals truly aren’t working. It’s an old piece of technology, and it was well used when it was new.&lt;/p&gt;

&lt;p&gt;Next up: &lt;a href="https://dev.to/dizzyspi/reversing-the-asciipad-sg-6-serial-link-part-2-28dc"&gt;part 2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reversing</category>
      <category>electronics</category>
    </item>
    <item>
      <title>Teaching A Machine To Identify Vulnerabilities (Part 3)</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 24 Jul 2022 23:36:00 +0000</pubDate>
      <link>https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-3-k1b</link>
      <guid>https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-3-k1b</guid>
      <description>&lt;p&gt;After blindly attempting to train the classifier (as seen in &lt;a href="https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-2-3d2j"&gt;Part 2&lt;/a&gt;), I had a good long hard think about all the variables and all the possible combinations of things I could try and tweak to get results out of my naive Bayes classifier. I also had a good long hard think about how I would measure those results. What are good results? This is an important thing to consider in machine learning. You may think that accuracy is all that matters — but that’s not the case.&lt;/p&gt;

&lt;p&gt;In this experiment, we’re attempting to classify observations into one of 34 different bins. If we look only at accuracy, we see only part of the picture. We can’t answer questions such as “is one category often misclassified as another category?” or “how many different categories does the classifier bin things into?” These questions have answers that are helpful in “debugging” our classifier, i.e. determining why it’s classifying things the way it is, and how we can improve our data and strategy in order to improve its accuracy. This is why confusion matrices are useful tools for visualizing the result of classification. Confusion matrices explain the how the errors in classification are distributed among the possible classes, shedding light on the overall behavior of the classifier.&lt;/p&gt;

&lt;p&gt;If you’ll recall, our dataset class distribution looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6QbGl6ao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uwv0a4azocelfgyejy1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6QbGl6ao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uwv0a4azocelfgyejy1m.png" alt="cwe-distribution" width="736" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The labels are heavily skewed toward CWE-121. A classifier could achieve high accuracy in this dataset by simply guessing CWE-121 for every observation. This is a type of overfitting. If the data does not have a lot of descriptive power, i.e. the features I’ve chosen to extract from the binaries are not related to the CWE classes, I would expect the classifier to have this behavior.&lt;/p&gt;

&lt;p&gt;To validate this, I made the decision to perform a trial where I trained the classifier on the dataset, but randomized the labels. This trial provides a baseline that can be inspected to compare subsequent, properly trained trials to in order to differentiate their results from random noise.&lt;/p&gt;

&lt;p&gt;With this in mind, I also identified a few different parameters to tweak in order to potentially improve classifier performance. We can perform classification at the basic block level or at the binary level. We can also choose to use a threshold to discard “uninformative” basic blocks, as described in the previous post. In addition, there is more than one naive Bayes classifier variant. Both Bernoulli naive Bayes and multinomial naive Bayes are applicable classifiers for our data. Finally, we can represent our features in two different ways: we can use a boolean representation for each opcode in each basic block, i.e. whether or not that opcode was present, or we can use an integer, i.e. how many times that opcode was present in the block. I attempted to exercise a reasonable combination of these parameters when training and evaluating the classifier.&lt;/p&gt;

&lt;p&gt;Throughout the rest of this post, you will see results for three separate classifiers; one a Bernoulli naive Bayes classifier trained on the binary-featured dataset, one a multinomial naive Bayes classifier trained on the binary-featured dataset, and one a multinomial naive Bayes classifier trained on the integer-featured (also referred to as “event-featured”) dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuning The Threshold
&lt;/h2&gt;

&lt;p&gt;As a refresher, in the previous post I talked about a clever way we could use the probability estimates returned by naive Bayes to identify basic blocks that are common across many CWE classes vs. those that are unique to a CWE class. The idea is that if the classifier thinks each of the classes is equally likely for a block, then that block is not informative for classification and can be discarded. Conversely, if the classifier is very confident that a block belongs to a particular class, then it is informative and should be kept.&lt;/p&gt;

&lt;p&gt;If we’re going to use this idea to discard basic blocks, we first need a way to measure this difference in probabilities between the classes. I chose to use the difference between the highest probability class and lowest probability class returned by the classifier. If the difference is small, the distribution is relatively even across the classes — if the difference is large, it is likely to be uneven. Semi-formally:&lt;/p&gt;

&lt;p&gt;m=x_highest — x_lowest&lt;/p&gt;

&lt;p&gt;Now that we have a metric, we need to know at what value blocks become “informative” vs. “uninformative.” In other words, we need to know what our threshold is. A good way to determine this is by training the classifier and seeing how classification performs with respect to different threshold values. This is a type of classifier tuning similar to that done by K-nearest Neighbors when choosing a value for K. This type of tuning requires a validation data set.&lt;/p&gt;

&lt;p&gt;Typically, when training a classifier, you need at least two datasets: a training data set, with which to train your classifier, and a testing dataset, to evaluate your classifier’s accuracy on. But when you want to test your trained classifier in under different conditions and pick the best condition as a parameter of your classifier, you need an additional dataset called a validation set in order to maintain the independence of the testing data from the process of training. Otherwise, you risk overfitting your classifier to your data, and will likely produce results in your experiment that translate poorly to unseen data.&lt;/p&gt;

&lt;p&gt;To tune the threshold, I trained each of the three classifiers on their respective data and plotted their accuracy with respect to different threshold values. I created two plots — one where the accuracy was calculated with respect to basic block level classification, and one where the accuracy was calculated by having the “informative” basic blocks “vote” on what they believed the binary they came from should be labeled as.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w_89tQb6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tmjkxs22ia6jg1sv62ep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w_89tQb6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tmjkxs22ia6jg1sv62ep.png" alt="graph-1" width="880" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accuracy curve with respect to threshold for each naive Bayes classifier applied to basic blocks&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dOby8eBE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ki15ff97xbl02rcj20if.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dOby8eBE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ki15ff97xbl02rcj20if.png" alt="graph-2" width="880" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accuracy curve with respect to threshold for each naive Bayes classifier applied to binaries&lt;br&gt;
The accuracy curve per basic block looks kind of like garbage — each of the classifiers performs fairly differently. However, when voting is applied to classify whole binaries, we see each classifier’s accuracy peaks at a threshold value of about 0.5. Therefore, we choose this as our threshold value for each classifier.&lt;/p&gt;

&lt;p&gt;Also, for fun, I plotted the distribution of the threshold metric. If I’m right about some basic blocks being common to all the CWE classes and others being unique, we should see a bimodal distribution of this metric.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PvbbCR60--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wmryoemqy7ookm3mlteu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PvbbCR60--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wmryoemqy7ookm3mlteu.png" alt="bargraph-1" width="880" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Threshold metric distribution for Bernoulli naive Bayes on binary-featured dataset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NiH6Sw9t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kl6u3o647frzdjjczvob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NiH6Sw9t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kl6u3o647frzdjjczvob.png" alt="bargraph-2" width="880" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Threshold metric distribution for multinomial naive Bayes on binary-featured dataset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JFFi9pM9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1srxcz2s5p5wwbviobo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JFFi9pM9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1srxcz2s5p5wwbviobo9.png" alt="bargraph-3" width="880" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Threshold metric distribution for multinomial naive Bayes on integer-featured dataset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The bimodal distribution hypothesis holds, if only barely. It’s not very pronounced, but it can be seen in each of the three distributions. This is useful information to have, because we could have chosen other metrics by which to create a threshold. It’s possible that another metric would plot a more emphatic bimodal distrbution. Likely such a metric would perform better as a threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calculating The Baseline
&lt;/h2&gt;

&lt;p&gt;The next thing to do is calculate the accuracy and confusion matrices for the baseline trial with the labels in the dataset randomized. I calculated this for each of the three classifiers, and each returned a very similar confusion matrix. One is shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8hxSVEVS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dgz8533ci9ef45ytdu1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8hxSVEVS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dgz8533ci9ef45ytdu1o.png" alt="confusion-1" width="880" height="784"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An example confusion matrix produced from random noise baseline testing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice that the classifier is classifying nearly everything as CWE-121, which is exactly as expected. We’ll try to improve on this overfitting with our different training strategies.&lt;/p&gt;

&lt;p&gt;The accuracy of each classifier under different conditions was also pretty similar between classifiers in the random trials. To calculate these accuracies, twenty trials were run. The data was randomized differently for each trial and split into training, validation, and testing datasets containing 1/3 of the data each.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1B69pzTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s8af6ms1c0g4aj1515uf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1B69pzTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s8af6ms1c0g4aj1515uf.png" alt="accuracy-1" width="880" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Baseline accuracies for the different classifiers, trained on randomly labeled data&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unsurprisingly, using a threshold on the randomly labeled data reduces the accuracy by an enormous amount. And while it’s not shown here, it’s also worth noting that using a threshold on this data reduces the dataset by a significant amount. We expect more of the data to be preserved during normal trials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training The Classifiers
&lt;/h2&gt;

&lt;p&gt;Finally we’re ready to evaluate our classifiers on real data with some different parameters. I evaluated each classifier under three different conditions — first, training and testing for basic block classification without any threshold value, second, basic block classification with a threshold value of 0.5, and third, whole binary classification with a threshold value of 0.5 using the basic blocks to vote on a label for their binary.&lt;/p&gt;

&lt;p&gt;Each of these conditions were tested with twenty trials of randomized data, training/validation/testing split of 1/3 of the dataset for each. The results for each of the three classifiers are shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R6JPiT2u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/touf9autp5w9dlh3kycp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R6JPiT2u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/touf9autp5w9dlh3kycp.png" alt="threshold-1" width="880" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bernoulli naive Bayes accuracy on binary-featured dataset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wgv2oWXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6vbvptt8p0zjd7zm0jkk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wgv2oWXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6vbvptt8p0zjd7zm0jkk.png" alt="threshold-2" width="880" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Multinomial naive Bayes accuracy on binary-featured dataset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WYYQhjR_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n1b3d320v413sgyx3omn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WYYQhjR_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n1b3d320v413sgyx3omn.png" alt="threshold-3" width="880" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Multinomial naive Bayes accuracy on integer-featured dataset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first thing you’ll notice is that the accuracy of each of the classifiers at basic block classification is lower than the random noise. This seems like a bad sign, but it is also odd. Any sort of significant deviation from the random baseline implies that the classifier is picking some kind of pattern out of the data. We turn to the confusion matrices to try to diagnose the difference between the random baseline and the actual run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lqymhfor--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5b9inm6cdshhq8gg2db2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lqymhfor--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5b9inm6cdshhq8gg2db2.png" alt="confusion-2" width="880" height="777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Confusion matrix for Bernoulli naive Bayes applied to binary-featured dataset for basic block classification&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While the random baseline classifier overfit to CWE-121, there is some evidence here that our properly-trained naive Bayes classifier does not overfit as strongly. In particular, CWE-119 and CWE-416 are guessed quite often as well. In addition, we are able to correctly classify a significant number of blocks from CWE-416 and CWE-122, in addition to CWE-121. Unfortunately, this also causes many CWE-121 basic blocks to be incorrectly guessed as other classes. Realizing that this is likely due to the poor labeling of the dataset, it seems we can say that there is some predictive value in the opcode features extracted from the basic blocks, though there’s too much noise for the classifier to produce an acceptable accuracy.&lt;/p&gt;

&lt;p&gt;The other unfortunate observation about the classifier accuracies is that applying a threshold does not increase the accuracy of the classifier. In the best case, it only reduces it by about 0.01 — which is a marked improvement over the baseline, but not terribly helpful. Voting even further decreases the accuracy, debunking the theory that we can use the probability outputs from naive Bayes for whittling down the list of informative basic blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaways
&lt;/h2&gt;

&lt;p&gt;As with all good science, just because something doesn’t work out the way you want or expect it to, doesn’t mean there isn’t a reason. I looked through the documentation for the naive Bayes classifier implementation I used from scikit-learn, attempting to find something to help me gain some insight into the probability outputs, and ran across this lovely gem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f0TD_h17--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/80aixyqapy69yi69v8xm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f0TD_h17--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/80aixyqapy69yi69v8xm.png" alt="docs" width="786" height="49"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;That's a note from &lt;a href="https://scikit-learn.org/stable/modules/naive_bayes.html"&gt;sckit-learn.org&lt;/a&gt; about naive Bayes classifiers.&lt;/p&gt;

&lt;p&gt;…so the idea of using the probabilities is fundamentally flawed, and in order to implement this, I need to use another classifier. Back to the drawing board. However, this experiment hasn’t been a waste. I’ve learned valuable information about my data composition, and eliminated a possible classifier from the list of candidates. Better, I learned that the data does have some informative value! Next up is identifying an appropriate replacement classifier and continuing the research.&lt;/p&gt;

</description>
      <category>security</category>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Teaching A Machine To Identify Vulnerabilities (Part 2)</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 24 Jul 2022 23:33:00 +0000</pubDate>
      <link>https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-2-3d2j</link>
      <guid>https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-2-3d2j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a writeup I've moved here from my personal website. It was originally published on 4/13/19&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-1-589p"&gt;previous post&lt;/a&gt;, I talked about the data processing needed to turn a bunch of binaries into a dataset for use with a machine learning classifier. While I said that talking through machine learning basics is out of scope for this series, I do want to talk through a bit about how the Naive Bayes classifier works, why I’ve chosen it, and how I plan to exploit the particular way in which it handles my data to do some cool things.&lt;/p&gt;

&lt;p&gt;If you recall, we have a dataset whose rows looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dp7jSEFp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2x935q6acsy0bkipsfc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dp7jSEFp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2x935q6acsy0bkipsfc7.png" alt="feature-row" width="511" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Naive Bayes is an inference classification algorithm. Inference relies on logical properties in order to provide a guess as to how likely it is that a given observation should belong to a particular class. In our case, Naive Bayes will be telling us how likely it is that a particular basic block belongs to one of our CWE classes. It will be doing this by looking at the opcodes present in that basic block, comparing that to basic blocks that it saw when it was trained, and returning a probability for each possible CWE class indicating how likely it is to be that CWE. We can use this to do something clever.&lt;/p&gt;

&lt;p&gt;There is something fundamentally wrong with the data. If you haven’t spotted it already, don’t worry, it’s subtle. When I created the dataset, I chose to create many observations from a single binary. Namely, I created one observation for each basic block in that binary. Vulnerabilities are generally localized to a small set of the basic blocks within a binary — however, I have labeled entire binaries with a CWE class. This means that there are many basic blocks, i.e. observations, which are incorrectly labeled, as they do not actually contain a vulnerability. That poor labeling is going to affect the training of the classifier, and in turn, adversely affect accuracy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UzqkGlwL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c1k01hi4ocs0ezd6vo2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UzqkGlwL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c1k01hi4ocs0ezd6vo2f.png" alt="classification-simplification" width="813" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Knowing what I do about binaries, though, I can use our classifier to cheat a little. What I know is that binaries often have some similar code, e.g. setup and teardown code, string manipulation code, console or network code, etc. Binaries which serve similar purposes often use similar code and therefore share more similar blocks, and conversely binaries which do drastically different things have less blocks in common. I make the hypothesis that blocks that are common amongst binaries from many different CWE classes are not indicative of a vulnerability, whereas blocks which are more unique to a CWE class are good indicators of that vulnerability. Because naive Bayes is an inference algorithm, and it returns for us a probability for each CWE class when classifying a basic block, we can inspect this probability distribution at classification time and determine if that block skews heavily toward a single class or if the classifier thinks it’s seen it before amongst many different classes. For example…&lt;/p&gt;

&lt;p&gt;If we have five different CWE classes we are classifying across, a block which has been seen before in all five CWE classes might return a probability from naive Bayes something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class 1: 20%
Class 2: 23%
Class 3: 21%
Class 4: 19%
Class 5: 17%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the other hand, a basic block that has only been seen before in one category might return a distribution like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class 1: 2%
Class 2: 3%
Class 3: 93%
Class 4: 1%
Class 5: 1%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s worth noting that while I say “a basic block that has been seen…”, in reality any basic block that is similar-ish to a basic block that’s been seen before, i.e. differs in handful of features, will be classified in the same way. That’s the power of machine learning — we train our classifier to recognize things it’s never seen before, using data that is hopefully representative of the problem space.&lt;/p&gt;

&lt;p&gt;So now that we understand how the classifier is being used, let’s give it a go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Trial 1: Start Simple
&lt;/h2&gt;

&lt;p&gt;For the first attempt, I kept everything as simple as possible. I ignored all of the clever stuff I mentioned about using probabilities to classify blocks as significant or not. While I had intuition that those things would be important, it’s important to challenge and validate your assumptions before acting on them. So for this trial, I randomized over the entire dataset, not bothering to keep samples from the same binary together, and I simply had my classifier predict the CWEs it thought each block had.&lt;/p&gt;

&lt;p&gt;This did not go well.&lt;/p&gt;

&lt;p&gt;I mean it could have gone worse. My classifier achieved a ~25% accuracy, predicting across 27 CWE classes. I plotted a confusion matrix, so I could see if it was doing anything shady, like only classifying samples as class 121 or 122 (because if you recall, those are the classes that the data was skewed toward).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zdQEpU6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qjj81dcvz32zgie0007j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zdQEpU6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qjj81dcvz32zgie0007j.png" alt="confusion-1" width="880" height="777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The errors are all over the place. Most classes do get misclassified as 121 more often than others, but not overly so. The fact that the errors are so spread out across the different classes is encouraging, because it may indicate that my approach has credence. It’s possible that the basic blocks that are getting misclassified are pretty common across most classes, and therefore get misclassified as any of them.&lt;/p&gt;

&lt;p&gt;To figure out if this was true, I took a look at the probabilities that were generated during classification. I wanted to get a general sense of what the probability spread looked like, so I calculated some basic stats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Highest probability ever seen: 0.9560759838758044
Lowest probabilities ever seen: 2.4691830828330134e-09
Average difference between in-sample highest and lowest probabilities: 0.32504693947187246
Standard deviation of in-sample probability differences: 0.06998244753425269
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hm. It seems like on average, the classifier is pretty confident about what it’s classifying. But the average could be skewed by it being confident classifying the blocks with vulnerabilities, and far less confident in the others. If that were the case, I’d expect to see the bi-modal distribution of the in-sample probability differences. So, let’s plot it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PhFnp--m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/usxl9jacr5f16fl8g9np.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PhFnp--m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/usxl9jacr5f16fl8g9np.png" alt="bargraph-1" width="880" height="221"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;That could somewhat be considered a bi-modal distribution. Not quite the separation that I would have hoped between the two modes, but there is certainly a second spike in the graph. You’ll notice that our average sits right in between the two spikes, at around 0.3.&lt;/p&gt;

&lt;p&gt;At this point, I still have no idea if the data points that have a higher probability difference actually classify better than the ones that do not. There’s one easy way to find out, though — try it. I wrote some code to drop classified blocks that had an in-sample probability difference of less than 0.4, and then looked at the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trial 2: Probability Inspection
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Correctly identified 90/347
Accuracy: 0.259365994236
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1xuz8wbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9tipcd095iuo6iipmjf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1xuz8wbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9tipcd095iuo6iipmjf5.png" alt="confusion-2" width="880" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not terribly convincing. Let’s try upping the threshold to 0.45.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Correctly identified 42/137
Accuracy: 0.306569343066
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--US73XJLF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xfi2ivm35lb9tpq4t66z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--US73XJLF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xfi2ivm35lb9tpq4t66z.png" alt="confusion-3" width="880" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accuracy is increasing, but not by as much as I’d like. And the confusion matrices still have the same general spread. It honestly doesn’t look much at all like the difference between the highest and lowest probability in a prediction is correlated at all with the accuracy of the classifier. I gave it another run at 0.5.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Correctly identified 17/67
Accuracy: 0.253731343284
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6w71vXvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7jtbryjr82oyte4tbyd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6w71vXvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7jtbryjr82oyte4tbyd8.png" alt="confusion-4" width="800" height="719"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FAp87SAH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5zc79ktksd8vzqpv3nyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FAp87SAH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5zc79ktksd8vzqpv3nyf.png" alt="confusion-4" width="800" height="719"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…and the accuracy went down. I’m willing to believe that it’s just not going to have an effect.&lt;/p&gt;

&lt;p&gt;It’s time to take stock of where we are and reform an approach. It appears that my approach utilizing the predicted probabilities to weed out uninformative basic blocks may not work, however I would like to try not splitting binaries across training and testing data to eliminate the possibility that this is preventing the classifier from learning what common basic blocks are. It seems unlikely, but it’s possible. It’s also possible that our data needs better labeling to be useful — this will take time, time that I don’t believe I have for this project. We can re-featurize the data to include counts of the number of times an instruction occurred in the basic block, rather than just whether or not it occurred. This will give the classifier more information to work with, which may improve its accuracy.&lt;/p&gt;

&lt;p&gt;Next up: &lt;a href="https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-3-k1b"&gt;part 3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Teaching A Machine To Identify Vulnerabilities (Part 1)</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 24 Jul 2022 23:32:00 +0000</pubDate>
      <link>https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-1-589p</link>
      <guid>https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-1-589p</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a writeup I've moved here from my personal website. It was originally published on 4/13/19&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the past couple of months, I’ve been heavily immersed in a graduate level machine learning course as part of my degree program. I haven’t been able to post about the cool work I’ve been doing because that would enable others to cheat (tsk tsk), but now I’m working on my final project and I would like to informally share my experience training a machine learning classifier to identify vulnerabilities in binaries.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before we get too deep into this, please be aware that this is an ongoing project and I do not currently know if this technique will work. This series is meant to provide some insight into the practical application of machine learning, regardless of whether or not the results are positive (that’s science!).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The machine learning techniques we covered in the course are considered classical machine learning. We covered supervised and unsupervised learning and implemented several well-known algorithms from scratch. I’m not going to teach you the basics of machine learning in this post — if you’d like to learn how to choose a classifier or how machine learning algorithms work, I recommend perusing &lt;a href="https://medium.com/machine-learning-in-practice/over-200-of-the-best-machine-learning-nlp-and-python-tutorials-2018-edition-dd8cf53cb7dc"&gt;this curated list of tutorials&lt;/a&gt;. Because the course focused on classical techniques, I chose to also focus my attention there and ignore the more shiny neural networks and deep learning options. My project sets out to show that a straightforward inference technique, Naive Bayes, can be used to provide valuable information for a real problem. That problem is identifying vulnerabilities in binary code.&lt;/p&gt;

&lt;p&gt;Binary code is the 0’s and 1’s that a program is made up of. If you’re a software developer, it’s the thing that your source code gets compiled into. If you’re a regular user, it’s the thing that you double click to launch an application. Binary code is made up of machine instructions that your CPU executes. These instructions essentially have two parts: the opcode, and any arguments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T0f09z4---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4g1e4ojmdo4pm4il13px.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T0f09z4---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4g1e4ojmdo4pm4il13px.png" alt="insn" width="304" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of these instructions and opcodes are packed into your binary file one right after the next. Different instructions take up more space than others, and their arguments can also be of varying length. If you open up a binary in a text editor, it looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2pOP6uMU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/irsgx26edzxn2os9vac8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2pOP6uMU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/irsgx26edzxn2os9vac8.png" alt="binary-junk" width="880" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the size of instructions and their arguments can vary, we need a special tool called a &lt;a href="https://en.wikipedia.org/wiki/Disassembler"&gt;disassembler&lt;/a&gt; to decode that binary file for us so that we can view the opcodes. Disassemblers do something else that’s cool, too, which is show you how logic flows through your program by decomposing it into blocks called “basic blocks” and drawing arrows between those blocks where there are jumps or conditionals that link them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--muQZHZNW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vwm90jzpp4i0z1k45glf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--muQZHZNW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vwm90jzpp4i0z1k45glf.png" alt="binja" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Some basic blocks from the “yolodex” CGC binary, as disassembled by &lt;a href="https://binary.ninja/"&gt;BinaryNinja&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My approach to discovering vulnerabilities relies on this block-level decomposition. I featurize binaries into basic blocks, enumerate the opcodes present in those blocks, and use that block-level data as one observation. I believe this may work because it is the series of instructions that a program executes that makes it vulnerable. Specific sequences, and their associated arguments, can leave a program vulnerable to buffer overflows or use-after-free or many other vulnerabilities. At the block-level, we have a fairly decent picture of which instructions are associated with one another in a sequence, and thus may be able to draw some conclusions about whether the program is vulnerable there.&lt;/p&gt;

&lt;p&gt;The different kinds of vulnerabilities that can crop up in programs are well enumerated by the &lt;a href="https://cwe.mitre.org/"&gt;Common Weakness Enumeration (CWE)&lt;/a&gt;. My approach attempts to classify which of these CWEs a given binary may manifest. Of course, to train a machine learning classifier to recognize vulnerabilities, I have to have some kind of training data set. I am using DARPA’s Cyber Grand Challenge set of Challenge Binaries as the basis for my training data. These binaries were written to contain vulnerabilities, and have readme files that describe the vulnerabilities in detail, including which CWEs they fall under. The first step on the road to creating my classifier is featurizing the binaries as I described above.&lt;/p&gt;

&lt;p&gt;As I said, a regular text editor won’t do for reading a binary file, so I needed to choose a disassembler to break the challenge binaries out into their basic blocks. I chose to use &lt;a href="https://binary.ninja/"&gt;Binary Ninja&lt;/a&gt; because it has a very easy-to-use Python API, and it’s hobbyist-level cheap (for comparison, the industry-standard disassembler is &lt;a href="https://www.hex-rays.com/products/ida/"&gt;IDA Pro&lt;/a&gt;, which they will sell to you for &lt;a href="https://www.hex-rays.com/cgi-bin/quote.cgi"&gt;roughly an arm&lt;/a&gt;, and continue to pick off your fingers and toes with renewal fees). I began by writing a quick script to go through a single binary and print out the opcodes it encountered in each block, just to validate that I was able to acquire the data I wanted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TX-CW9ru--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ohdz821x6zmp21bgr8b0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TX-CW9ru--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ohdz821x6zmp21bgr8b0.png" alt="binja-output" width="880" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The output of a Python script using Binary Ninja’s API to print basic blocks&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Awesome. Step 1 complete. But none of this data is labeled. I want to label my basic blocks with the CWE that the binary contains. The CWEs are all described in a bunch of README.md files with no standard format. I briefly considered writing a script to pull the CWE labels out of the READMEs, but decided that the amount of time I would spend debugging edge cases and validating that the script actually grabbed the correct CWE numbers would be at least as much as just doing it by hand. So away I went, plugging CWE numbers into a spreadsheet to create a CSV mapping binaries to their CWEs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2968Y-xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wwmjzowodozfvbmn3e4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2968Y-xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wwmjzowodozfvbmn3e4s.png" alt="cgc-labels" width="747" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spreadsheet of CGC binaries labeled with their CWEs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This took me some hours, of which I mostly spent watching YouTube (&lt;a href="https://www.youtube.com/user/bigclivedotcom"&gt;bigclive’s teardowns&lt;/a&gt; are always a good way to spend some time). Rote tasks are good opportunities to learn a thing or two. Or to watch a show, pick your poison.&lt;/p&gt;

&lt;p&gt;The first thing I noticed going through my labels is that some binaries are labeled with more than one CWE. This makes sense, but wasn’t something I had considered in my original approach. To simplify the problem, I made the (somewhat bad) decision to discard samples that are labeled with more than one CWE. There are better ways to handle this problem, but I only have three weekends to train, evaluate, and present my classifier, so I unfortunately do need to cut some corners. I calculated some basic statistics on my dataset to gain some insight into its composition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Number of binaries:
108
Number of unique CWEs:
27
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1nHa5zX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/im7y410kywisdj5acavu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1nHa5zX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/im7y410kywisdj5acavu.png" alt="bargraph-1" width="736" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Distribution of binaries with respect to CWEs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most of my samples are for CWEs 121 and 122. It’s important to note that this does not necessarily mean that my dataset is unrealistically biased. In fact, CWEs 121 and 122 are “Stack-based Buffer Overflow” and “Heap-based Buffer Overflow,” which are two very common vulnerabilities. That said, it’s worth being mindful of this skewing, as it will impact how our classifier trains.&lt;/p&gt;

&lt;p&gt;Putting it all together, we now have a dataset where each row describes the instructions in one basic block, labeled with the CWE represented by the binary that the basic block was taken from. We’re ready to try to train a classifier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5mE5raA_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h6u6pu7gwy710e5te837.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5mE5raA_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h6u6pu7gwy710e5te837.png" alt="rows" width="880" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sample rows from the featurized data CSV&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next up: &lt;a href="https://dev.to/dizzyspi/teaching-a-machine-to-identify-vulnerabilities-part-2-3d2j"&gt;part 2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>The argument for terminal multiplexers</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sat, 16 Jul 2022 15:12:27 +0000</pubDate>
      <link>https://dev.to/dizzyspi/the-argument-for-terminal-multiplexers-2g39</link>
      <guid>https://dev.to/dizzyspi/the-argument-for-terminal-multiplexers-2g39</guid>
      <description>&lt;p&gt;I went years without using a terminal multiplexer. I thought they were for kids who thought they were l33t hax0rz. My approach to tooling has always been minimalistic - I'm all for trying new things out, but if there's no significant improvement in my experience after a couple of days, I drop the tool.&lt;/p&gt;

&lt;p&gt;Despite the learning curve, I saw immediate improvements in my quality of life when I started using tmux. It was definitely a "why didn't I do this sooner moment."&lt;/p&gt;

&lt;p&gt;Embarrassing though it is in hindsight, I'll share how I used to work before learning tmux. I ssh into a dev VM for my work. I need multiple windows, so, I open up about six terminal instances and ssh each one to the dev VM. I'd use my window manager to snap the terminal windows how I liked them, and click between each one when I wanted to work in a different window. If I needed to suspend or reboot my machine, I'd close all of the ssh sessions, and then re-establish them next time, meaning I had to re-navigate to the files I was working on and redo any other working state in order to pick up my work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OJPPm0nO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aubqqdw06kx4c484g4z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OJPPm0nO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aubqqdw06kx4c484g4z7.png" alt="a screenshot of tmux with three panes open: one running vim, one running htop, and one with a prompt" width="880" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;^ What tmux looks like for me - here's &lt;a href="https://dev.to/dizzyspi/making-tmux-suck-less"&gt;a post on my customizations&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I cannot tell you how &lt;em&gt;painful&lt;/em&gt; that sounds now.&lt;/p&gt;

&lt;p&gt;Because tmux (or screen, or any other terminal multiplexer) runs on the machine you're ssh'd into, you can detach, close your ssh session, then re-connect later and pick up exactly where you left off. This nicety alone is worth it, and is why I decided to use tmux in the first place. But then you also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have the ability to switch windows using the keyboard (in a much more intelligent and seamless way than ctrl+tab).&lt;/li&gt;
&lt;li&gt;You can spawn and close new terminal sessions on the machine you're ssh'd into effortlessly, making it possible to open a disposable terminal to run a single command and then close it.&lt;/li&gt;
&lt;li&gt;Each new terminal pane will automatically start from a current working directory of wherever you established the tmux session, so if you cd into your project before starting tmux, every new window has a base directory there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But wait, there's even more! Say you have multiple inter-dependent projects. You can open a session for each one, and switch between those sessions easily. It's like the equivalent of swapping workspaces on your desktop. Super handy.&lt;/p&gt;

&lt;p&gt;Have I convinced you to try tmux? Check out what I did to &lt;a href="https://dev.to/dizzyspi/making-tmux-suck-less"&gt;make tmux work better for me&lt;/a&gt;, because out of the box it has a pretty terrible user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheatsheet
&lt;/h2&gt;

&lt;p&gt;Here are all the tmux commands I use to do the stuff I just talked about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Switch sessions&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;prefix&amp;gt; + s
arrow keys up and down to highlight session
enter to select session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New session&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tmux new -s &amp;lt;session name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New pane&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Split the current pane horizontally in half:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;prefix&amp;gt; + "
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Split the current pane vertically in half:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;prefix&amp;gt; + %
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Detach from session&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;prefix&amp;gt; + d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Attach to session&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



</description>
      <category>tooling</category>
      <category>linux</category>
    </item>
    <item>
      <title>Making tmux suck less</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sat, 16 Jul 2022 15:12:20 +0000</pubDate>
      <link>https://dev.to/dizzyspi/making-tmux-suck-less-97e</link>
      <guid>https://dev.to/dizzyspi/making-tmux-suck-less-97e</guid>
      <description>&lt;p&gt;tmux &lt;a href="https://dev.to/dizzyspi/the-argument-for-terminal-multiplexers"&gt;is a great tool&lt;/a&gt;. But it's basically unusable out of the box. Try it - fire it up in a bare environment without a tmux config file. You'll have no colors. You'll have weird keybindings issues. The vim clipboard will be broken. Copying text with your mouse will be broken (unless you really &lt;em&gt;did&lt;/em&gt; want to copy random bits of text from all of your panes, and not just the active one). In short, you'll have a terrible user experience.&lt;/p&gt;

&lt;p&gt;I foolishly thought tmux would inherit all of the niceties of my base terminal emulator. As it turns out, tmux behaves a lot more like a standalone terminal than an add-on to your existing one. I can respect a tool that assumes nothing about its use, but it does mean you have to do some configuring to make it work well for you. Here's what I did:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Enabling colors
&lt;/h2&gt;

&lt;p&gt;Add this to your .tmux.conf&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; default-terminal &lt;span class="s2"&gt;"xterm-256color"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Fixing weird keybind issues
&lt;/h2&gt;

&lt;p&gt;For me, adding this to my config solved it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;set-window-option &lt;span class="nt"&gt;-g&lt;/span&gt; xterm-keys 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's been a while, so I'm not totally sure what problem it was that it solved, but I &lt;em&gt;think&lt;/em&gt; &lt;a href="https://stackoverflow.com/questions/15445481/mapping-arrow-keys-when-running-tmux"&gt;it was this&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Highlighting and copying text with the mouse
&lt;/h2&gt;

&lt;p&gt;Credit to &lt;a href="https://unix.stackexchange.com/questions/318281/how-to-copy-and-paste-with-a-mouse-with-tmux"&gt;this SO post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this to your .tmux.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; mouse on
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; WheelUpPane &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-shell&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"#{mouse_any_flag}"&lt;/span&gt; &lt;span class="s2"&gt;"send-keys -M"&lt;/span&gt; &lt;span class="s2"&gt;"if -Ft= '#{pane_in_mode}' 'send-keys -M' 'select-pane -t=; copy-mode -e; send-keys -M'"&lt;/span&gt;
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; WheelDownPane &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="nt"&gt;-pane&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; send-keys &lt;span class="nt"&gt;-M&lt;/span&gt;
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; C-WheelUpPane &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="nt"&gt;-pane&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; copy-mode &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; send-keys &lt;span class="nt"&gt;-M&lt;/span&gt;
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi    C-WheelUpPane   send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-up
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi    C-WheelDownPane send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-down
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-emacs C-WheelUpPane   send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-up
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-emacs C-WheelDownPane send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and install xclip&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;xclip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turning mouse mode on will also enable you to scroll each pane with the mouse wheel, and select panes with the mouse.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Set prefix to `
&lt;/h2&gt;

&lt;p&gt;I prefer the prefix to be a single key. I thought using ` as the prefix and enabling entering it when pressing the key twice quickly was an elegant solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unbind C-b
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; prefix &lt;span class="sb"&gt;`&lt;/span&gt;
bind-key &lt;span class="sb"&gt;`&lt;/span&gt; send-prefix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Add a theme
&lt;/h2&gt;

&lt;p&gt;While not strictly necessary, adding a theme to tmux really does make it look way cooler. On a functional note, though, some themes update what information is shown in the bottom status bar by default, which can really improve the user experience.&lt;/p&gt;

&lt;p&gt;I went with a simple powerline theme from the &lt;a href="https://github.com/jimeh/tmux-themepack"&gt;tmux-themepack&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Here's my .tmux.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;set-window-option &lt;span class="nt"&gt;-g&lt;/span&gt; xterm-keys on
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; default-terminal &lt;span class="s2"&gt;"xterm-256color"&lt;/span&gt;
unbind C-b
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; prefix &lt;span class="sb"&gt;`&lt;/span&gt;
bind-key &lt;span class="sb"&gt;`&lt;/span&gt; send-prefix

source-file &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/repos/tmux-themepack/powerline/default/purple.tmuxtheme"&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; mouse on
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; WheelUpPane &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-shell&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"#{mouse_any_flag}"&lt;/span&gt; &lt;span class="s2"&gt;"send-keys -M"&lt;/span&gt; &lt;span class="s2"&gt;"if -Ft= '#{pane_in_mode}' 'send-keys -M' 'select-pane -t=; copy-mode -e; send-keys -M'"&lt;/span&gt;
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; WheelDownPane &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="nt"&gt;-pane&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; send-keys &lt;span class="nt"&gt;-M&lt;/span&gt;
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; C-WheelUpPane &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="nt"&gt;-pane&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; copy-mode &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; send-keys &lt;span class="nt"&gt;-M&lt;/span&gt;
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi    C-WheelUpPane   send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-up
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi    C-WheelDownPane send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-down
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-emacs C-WheelUpPane   send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-up
&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-emacs C-WheelDownPane send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; halfpage-down

&lt;span class="c"&gt;# To copy, left click and drag to highlight text in yellow, &lt;/span&gt;
&lt;span class="c"&gt;# once you release left click yellow text will disappear and will automatically be available in clibboard&lt;/span&gt;
&lt;span class="c"&gt;# # Use vim keybindings in copy mode&lt;/span&gt;
setw &lt;span class="nt"&gt;-g&lt;/span&gt; mode-keys vi
&lt;span class="c"&gt;# Update default binding of `Enter` to also use copy-pipe&lt;/span&gt;
unbind &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi Enter
bind-key &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi Enter send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; copy-pipe-and-cancel &lt;span class="s2"&gt;"xclip -selection c"&lt;/span&gt;
bind-key &lt;span class="nt"&gt;-T&lt;/span&gt; copy-mode-vi MouseDragEnd1Pane send-keys &lt;span class="nt"&gt;-X&lt;/span&gt; copy-pipe-and-cancel &lt;span class="s2"&gt;"xclip -in -selection clipboard"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tooling</category>
      <category>tutorial</category>
      <category>linux</category>
    </item>
    <item>
      <title>Cmocka Quick Start</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Tue, 07 Jun 2022 20:39:35 +0000</pubDate>
      <link>https://dev.to/dizzyspi/cmocka-quick-start-3n1i</link>
      <guid>https://dev.to/dizzyspi/cmocka-quick-start-3n1i</guid>
      <description>&lt;p&gt;Today I have occasion to learn a C unit test framework again. This comes about in my career every 3 years or so, so each time, I do a quick re-survey of what's available and pick something that looks good. This time, I've picked &lt;a href="https://cmocka.org/"&gt;cmocka&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main page links to the API documentation and a slideshow. Neither of these, in my quick perusal, actually give you an example of building a project with Cmocka. I really appreciate it when there's some kind of quickstart skeleton project you can just roll with, so I've taken their &lt;a href="https://api.cmocka.org/"&gt;first example from the API&lt;/a&gt; and done just that.&lt;/p&gt;

&lt;p&gt;The first thing you'll want to do to follow along here is download cmocka. I grabbed the &lt;a href="https://git.cryptomilk.org/projects/cmocka.git/"&gt;source release of 1.1.5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Untar the archive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; cmocka-1.1.5.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;build the cmocka library. For my project, I want the cmocka library to be statically compiled, so I configure cmocka with cmake accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;cmocka-1.1.5
&lt;span class="nb"&gt;mkdir &lt;/span&gt;build
&lt;span class="nb"&gt;cd &lt;/span&gt;build
cmake &lt;span class="nt"&gt;-DWITH_STATIC_LIB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ON ..
make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cmocka will have built the &lt;code&gt;libcmocka-static.a&lt;/code&gt; library to the &lt;code&gt;build/src&lt;/code&gt; directory. This is what you'll need to link your tests against to get them to build. Using the static cmocka library means that the library will be copied into the binary that's created. This makes it so that the C interpreter doesn't have to find libcmocka at runtime. This is handy if you don't want to install the libcmocka library on your system, or if you're going to be running your tests in an environment other than you development environment. Be aware though that this does make your binary larger and has other portability concerns attached to it, so in lots of (production) cases, static libraries are &lt;em&gt;not&lt;/em&gt; the way to go.&lt;/p&gt;

&lt;p&gt;Next, make yourself a &lt;code&gt;test.c&lt;/code&gt; file with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdarg.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stddef.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;setjmp.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;cmocka.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="cm"&gt;/* A test case that does nothing and succeeds. */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;null_test_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* unused */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CMUnitTest&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cmocka_unit_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;null_test_success&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cmocka_run_group_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and build your test file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-I&lt;/span&gt; cmocka-1.1.5/include/ &lt;span class="nt"&gt;-L&lt;/span&gt; cmocka-1.1.5/build/src/ test.c &lt;span class="nt"&gt;-l&lt;/span&gt;:libcmocka-static.a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will have produced a &lt;code&gt;a.out&lt;/code&gt; binary. Run it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./a.out 
&lt;span class="o"&gt;[==========]&lt;/span&gt; Running 1 &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; RUN      &lt;span class="o"&gt;]&lt;/span&gt; null_test_success
&lt;span class="o"&gt;[&lt;/span&gt;       OK &lt;span class="o"&gt;]&lt;/span&gt; null_test_success
&lt;span class="o"&gt;[==========]&lt;/span&gt; 1 &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; run.
&lt;span class="o"&gt;[&lt;/span&gt;  PASSED  &lt;span class="o"&gt;]&lt;/span&gt; 1 &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you go. If you want to write unit tests for real C code, the only difference is you need to &lt;code&gt;#include&lt;/code&gt; your headers for the source you're testing, and compile the source files you're testing along with the tests.&lt;/p&gt;

</description>
      <category>c</category>
      <category>testing</category>
    </item>
    <item>
      <title>Creating A Spotify to YouTube Music Playlist Converter</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 12 Sep 2021 02:55:02 +0000</pubDate>
      <link>https://dev.to/dizzyspi/creating-a-spotify-to-youtube-music-playlist-converter-f39</link>
      <guid>https://dev.to/dizzyspi/creating-a-spotify-to-youtube-music-playlist-converter-f39</guid>
      <description>&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%2Fvwhxhswbwzs7cj8jomkd.jpg" 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%2Fvwhxhswbwzs7cj8jomkd.jpg" alt="Headphones plugged into a phone"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@kaboompics?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Kaboompics.com&lt;/a&gt; from &lt;a href="https://www.pexels.com/photo/black-headphones-with-mobile-smartphone-6320/?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Just want the code? Got ya covered. &lt;a href="https://github.com/dizzyspiral/spotify-to-youtube" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Right. So I've been using Spotify for some years now. I have a collection of carefully curated playlists and liked music. I'm loathe to move off the platform and start over with another platform. But, YouTube music comes free with my YouTube premium subscription... so, it's a bit silly for me to keep shelling out $$ to Spotify each month. What a conundrum.&lt;/p&gt;

&lt;p&gt;Doing a quick search, there are plenty of third-party apps that purport to transfer your playlists for you. You just log into your Spotify and YouTube accounts through their webapp and away you go. I find this kind of service... sketchy, though - you're essentially giving that site your logins/auth tokens for your music accounts. The service is free, so I'd put money on them scraping your accounts for metadata they can sell. And if you've read any of my other posts, you probably know how much this irks me. I value my privacy. And perhaps, you value yours.&lt;/p&gt;

&lt;p&gt;The good news is these webapp services aren't doing anything that we can't do ourselves. They're accessing the Spotify and YouTube developer APIs to grab data from one account and shove it into the other. Building a tool to do this from scratch wouldn't take too long, probably. But this seems like something a lot of people would find useful - which makes it super likely someone in the open source community has already done something like it. Let's check github.&lt;/p&gt;

&lt;p&gt;There's this project, &lt;a href="https://github.com/watsonbox/exportify" rel="noopener noreferrer"&gt;exportify&lt;/a&gt;, that exports playlists from Spotify into CSV format. This sounds great. CSVs are easy to work with, so we should be able to take this data and import it into YouTube music, even if we have to do a little massaging to make it the right format.&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%2Fxjsf98uxj8rb51fcrui1.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%2Fxjsf98uxj8rb51fcrui1.png" alt="image"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;We could download their source and try to make it run, but it looks like the developer hosts the webapp &lt;a href="https://watsonbox.github.io/exportify/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. While this is basically the same setup as the third party web apps I detest, I have &lt;em&gt;a lot&lt;/em&gt; more trust in the open source community than I do random results from Google. I'll use it.&lt;/p&gt;

&lt;p&gt;Okay, after logging into my Spotify account via exportify, I get a list of all my playlists. I grabbed my liked songs and a couple other playlists. The CSVs are full of all sorts of spotify-specific data that we probably don't need.&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%2F0hfg36kcbiigoedn7bwx.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%2F0hfg36kcbiigoedn7bwx.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a look at what formats YouTube Music accepts for import. Oh, ugh. YouTube Music doesn't appear to offer any sort of convenient import function. What's more is it &lt;a href="https://www.reddit.com/r/YoutubeMusic/comments/jwpd1m/youtube_music_api/" rel="noopener noreferrer"&gt;doesn't seem to have any API of its own&lt;/a&gt; - the YouTube API is basically also its API, maybe. That seems about right for Google, which is making all the same mistakes that Microsoft did back in the 90s.&lt;/p&gt;

&lt;p&gt;Anyway, I digress. There exists this &lt;a href="https://ytmusicapi.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;unofficial API&lt;/a&gt;, which we might be able to use. Let's give it a try.&lt;/p&gt;

&lt;p&gt;The API is python (yessss), so we'll make a virtual environment for playing with the API. This is always good practice, as it keeps one project's dependencies from interfering with another. If you install all of your python packages to your global python install, after a while you can end up with conflicts that break things in weird and unexpected ways. Just better to compartmentalize.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virtualenv -p python3 venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That creates a virtual environment for python3 in a new directory, venv. I'm on Linux, so that's what the terminal command looks like. I haven't used Windows seriously in a very long time - if you're a Windows or Mac user, you're gonna have to find your own way (on Mac it's probably the same thing).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will activate the virtual environment. Now we're playing in the sandbox with a fresh python3 install. Let's grab the unofficial YouTube Music API package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install ytmusicapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the rest of the &lt;a href="https://ytmusicapi.readthedocs.io/en/latest/setup.html" rel="noopener noreferrer"&gt;setup instructions&lt;/a&gt; to get an authenticated session. You can press ctrl+shift+c in Chrome to get to the developer console, and click on the Network tab to see the network traffic. You may have to click on some content or search for some music before you'll have the appropriate POST request to snag.&lt;/p&gt;

&lt;p&gt;Their instructions were a bit unclear to me - copy-pasting all the header data from &lt;code&gt;accept&lt;/code&gt; onward didn't really work... probably because without some tweaking, the copy-paste content is an improperly formed JSON file. All you actually need for auth is the cookie, so I used the "manual file creation" method and pasted in my own cookie, and that worked fine.&lt;/p&gt;

&lt;p&gt;Now that we're auth'd, we can play around with the API. I tried a quick song search to verify that the API seemed to work as expected, using a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ytmusicapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YTMusic&lt;/span&gt;
&lt;span class="n"&gt;ytmusic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;YTMusic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth_file.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Oasis Wonderwall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It barfed back some results in JSON format. Lovely.&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%2F7iyyire4pp8h6bp9r64n.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%2F7iyyire4pp8h6bp9r64n.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wandering through the &lt;a href="https://ytmusicapi.readthedocs.io/en/latest/reference.html" rel="noopener noreferrer"&gt;reference for the API&lt;/a&gt;, I'm particularly interested in &lt;code&gt;create_playlist&lt;/code&gt; and &lt;code&gt;add_playlist_items&lt;/code&gt;. I'll try creating a test playlist, and see if it shows up in my account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ytmusicapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YTMusic&lt;/span&gt;
&lt;span class="n"&gt;ytmusic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;YTMusic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/dizzyspiral/projects/spotify-to-youtube/auth_file.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;playlist_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_playlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test Playlist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Testing 1, 2, 3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playlist_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked. Excellent. Now, about &lt;code&gt;add_playlist_items&lt;/code&gt;... It requires a YouTube video ID in order to add a song, so it looks like we'll have to first search for a song, grab the corresponding music video ID, and add it to the playlist using that. But when we searched for Wonderwall, we got a &lt;em&gt;bunch&lt;/em&gt; of results. How do we know which one is the "right" one...&lt;/p&gt;

&lt;p&gt;Nothing for it - we don't. But the search results are ordered from most to least relevant, with the "top result" labeled in the JSON and appearing first. I'll just trust that that's the right one.&lt;/p&gt;

&lt;p&gt;Okay, now we come back around to our exported Spotify playlists. We need the song title and artist in order to search for the song using ytmusicapi. Each row of the exported playlist CSV is a song, and the track name and artist name are in the second and fourth fields, or indices 1 and 3, respectively. Python has a csv module for handling CSVs. We'll write a little code to import and read the CSV.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ytmusicapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YTMusic&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_spotify_playlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playlist_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playlist_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quotechar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{}{}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;songs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we noted earlier, the top result is returned first by the search API call. But, the very first result is some kind of "top result" result, which actually doesn't contain a video ID. And we need the video ID. The second result should have it. (My guess here is that the "top result" thing is just metadata for displaying the top result separately from the rest of the results, since these "API" calls are really only intended to be used by the actual YouTube Music app(s).)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_for_songs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;search_strings&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;video_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;search_strings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;video_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;videoId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;video_ids&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, to put it all together, we'll make a main function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;  &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connecting to YouTube Music...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ytmusic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;YTMusic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth_file.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;playlist_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exported_playlist.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading exported Spotify playlist...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_spotify_playlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playlist_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Searching for songs on YouTube...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;video_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_for_songs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creating playlist...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# See if the playlist exists already before creating it
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Some Playlist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;playlists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;library&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;playlist_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;browseId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;playlist_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_playlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Some Playlist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Some description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Adding songs...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;video_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;video_ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ytmusic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_playlist_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playlist_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Done!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                         
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the API claims that &lt;code&gt;add_playlist_items&lt;/code&gt; takes a list of strings as an argument, for whatever reason, that doesn't actually work. It silently fails to add any songs. So, we just iterate through our list of strings, artificially making them a list of one for each API call. It takes a bit longer, but it gets the job done.&lt;/p&gt;

&lt;p&gt;And that's it. It's not pretty, but it was a one-day hack. I'll probably toss the code on github after cleaning it up a bit.&lt;/p&gt;

&lt;p&gt;Cheers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: &lt;a href="https://github.com/dizzyspiral/spotify-to-youtube" rel="noopener noreferrer"&gt;Code is here.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Making a COVID warning light</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 20 Dec 2020 17:07:55 +0000</pubDate>
      <link>https://dev.to/dizzyspi/making-a-covid-warning-light-33f2</link>
      <guid>https://dev.to/dizzyspi/making-a-covid-warning-light-33f2</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is designed for beginners at Linux and circuits. If you've done a project like this before, maybe just skip to the &lt;a href="https://github.com/dizzyspiral/covidlight" rel="noopener noreferrer"&gt;code&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With COVID-19 on the upswing again, I thought it would be nice to build a small appliance that could tell me how bad it is outside, without having to look up the local statistics. Here's what I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.adafruit.com/product/3400" rel="noopener noreferrer"&gt;1x Raspberry Pi Zero W&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://smile.amazon.com/KEXIN-Micro-MicroSDHC-UHS-I-Memory/dp/B085ZVG84C/" rel="noopener noreferrer"&gt;1x 8GB micro SD card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1x red LED&lt;/li&gt;
&lt;li&gt;1x yellow LED&lt;/li&gt;
&lt;li&gt;2x 200 ohm resistor&lt;/li&gt;
&lt;li&gt;1x breadboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The general idea is to create a device that scrapes the local COVID infection data and turns on a yellow light if the situation deserves a warning, and red if the situation is dire. We'll define what those situations mean in terms of data later.&lt;/p&gt;

&lt;p&gt;Because web scraping is full of sadness, we'll be using the reliable covidtracking.com API. This will give us data at state granularity. As a bonus, if you live in the US, you can easily follow along and make one of these to track the pandemic in your state.&lt;/p&gt;

&lt;p&gt;Let's start by building out the Pi. Grab a copy of &lt;a href="https://www.raspberrypi.org/software/operating-systems/" rel="noopener noreferrer"&gt;Raspberry Pi OS Lite&lt;/a&gt; and &lt;a href="https://www.balena.io/etcher/" rel="noopener noreferrer"&gt;Balena Etcher&lt;/a&gt;. Decompress the Raspberry Pi OS image. Insert your SD card in your computer, run Balena, and flash the decompressed image to your SD card. You should end up with an SD card that now has boot and rootfs paritions. Congratulations, you've installed Raspberry Pi OS for your Raspberry Pi Zero W.&lt;/p&gt;

&lt;p&gt;That doesn't do us much good if we can't log into the pi once it's booted, so before we eject the SD card from our computer, let's &lt;a href="https://desertbot.io/blog/setup-pi-zero-w-headless-wifi" rel="noopener noreferrer"&gt;set up networking&lt;/a&gt;. Navigate to the boot partition of SD card. Create an empty file called "ssh" in the top-level of the boot partition. If you're on linux, you can do this in the terminal with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This will let us log into the pi using an SSH client after it's booted. Next, let's give the pi the network configuration for our local wireless network. Create and open the file "wpa_supplicant.conf" in the top-level of the boot partition on the SD card. Copy and paste in the following contents, replacing the SSID and PSK with that of your wireless network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="NETWORK-NAME"
    psk="NETWORK-PASSWORD"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go ahead and eject your SD card, and install it in the pi zero W. Power it up, and it should come online. If your local network has local DNS, you can probably wait a few minutes and then log in using the pi@raspberrypi as the address. But for most of us, we'll need an IP address. How you determine the IP address of our Pi Zero W will vary depending on your local router, but generally you can go to 192.168.1.1 in your web browser, and it will bring you to a log-in page for your router. Log in (if you don't know what the credentials are, try looking on the bottom of the router, or searching for "default username and password [router brand]". Often, it's admin:admin or admin:password"). Look for a page that shows the active DHCP leases. If you see one for a device called raspberrypi, that's probably it! If you don't see any hostnames, grab whatever the most recently leased IP is. Try that with the instructions below, and if it doesn't work, grab the next most recent, and so on. If you still can't get it, power down the Pi and pop the SD card back in your computer, and make sure you got the wireless network credentials right.&lt;/p&gt;

&lt;p&gt;Once you have your Pi's IP address, log into it using an SSH client. If you're on Windows, you might choose &lt;a href="https://www.putty.org/" rel="noopener noreferrer"&gt;Putty&lt;/a&gt;. If you're on Linux, just open up the terminal and run the following, replacing raspberrypi with your Pi's IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh pi@raspberrypi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default password for the pi user is "raspberry". You should change that once you've logged in. Do that with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;and change the password as prompted, to whatever you want, so long as you'll remember it.&lt;/p&gt;

&lt;p&gt;Since our Pi Zero W is a headless unit, meaning it only provides a terminal and not a desktop environment, we're going to need to do all of our development work in the SSH session, without a GUI. Since I've already written all of the code, you can skip this if you just want to clone the repo and aren't going to try hacking it with your own changes. But if you are going to make some tweaks, grab yourself a terminal editor. Nano is easy to get started with, but personally I'm a big fan of vim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; vim is for advanced users, it takes some getting used to. But it's really powerful, and once you learn it, you'll have a great tool in your arsenal that works on almost any PC.&lt;/p&gt;

&lt;p&gt;We'll start by writing the code to get the current COVID data. To get an idea what we're working with, let's grab the COVID data for New York State (if you want to follow along using a different state, just replace "ny" with your state's code in the URL):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget https://api.covidtracking.com/v1/states/ny/daily.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads a file called "daily.json" to the current directory. Let's open it and see what we're working with:&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%2Fi%2Flrl42vo3f5v8rjeysm4h.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%2Fi%2Flrl42vo3f5v8rjeysm4h.png" alt="JSON file contents"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay. How to turn this into an indicator? I chose to use the positivity rate with respect to testing because it was easy to calculate and, for my area, testing has stabalized, i.e. if you want a COVID test, you can get one. This means that the positivity rate &lt;em&gt;should&lt;/em&gt; roughly track any upticks or downswings in COVID infections at large. There are more robust ways to track the outbreak, such as inspecting the death rate, hospitalizations, etc., but they are more complicated and require tracking the data over time. For now, I have opted not to do that.&lt;/p&gt;

&lt;p&gt;Our positivity rate can be calculated with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;positivity rate = new positive cases / total new cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arbitrarily, I've decided that a positivity rate of 2% will turn on the yellow light, and a positivity rate of 5% will turn on the red one.&lt;/p&gt;

&lt;p&gt;Let's write the code.&lt;/p&gt;

&lt;p&gt;We're gonna use Python because we don't hate ourselves. Python's great for tiny little projects like this. It's quick to develop in and requires very little of the developer - almost everything you need to do can be done with an imported library. In this case, we're going to use the &lt;code&gt;gpiozero&lt;/code&gt; package to control the LEDs, &lt;code&gt;requests&lt;/code&gt; to fetch the web data, and &lt;code&gt;json&lt;/code&gt; to parse it. &lt;code&gt;gpiozero&lt;/code&gt; requires some dependencies. Let's install those:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt install pigpiod python-pip
pip install gpiozero
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can start working on our code. Like I said, I use vim for editing, but please use whatever you're comfortable with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim covidwarn.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll start by importing the modules we'll need. Put this at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from gpiozero import LED
from time import sleep
import requests
import json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, write the function to get the data from the covidtracking API and calculate the positivity rate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_data():
    r = requests.get('https://api.covidtracking.com/v1/states/ny/current.json', allow_redirects=True)
    j = json.loads(r.content)

    positivity_rate = float(j["positiveIncrease"]) / float(j["totalTestResultsIncrease"])

    return positivity_rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the requests.get(...) call does the same thing that wget did - it grabs the JSON-formatted COVID tracking data from the internet and downloads it into variable &lt;code&gt;r&lt;/code&gt;. Since &lt;code&gt;r&lt;/code&gt; is just a raw string, it's not very convenient for parsing. So we load it as a JSON object and turn it into a map, so that we can access different parts of the data by providing the key associated with it. A key is just a string that comes before a value in the string - in this case, we're using the keys "positiveIncrease" to grab the number of new positive tests, and "totalTestResultsIncrease" to grab the total number of new tests. If you stare at the original download from wget, you'll see these strings in there, and you'll see some numbers come after them.&lt;/p&gt;

&lt;p&gt;Now we have a function that grabs data from the web, calculates the positivity rate, and returns it. Next is to create something that will use that to light the LEDs.&lt;/p&gt;

&lt;p&gt;Let's start by initializing our pi to a known state and setting up some constant variables. We want to make sure both of our LEDs are off to start with, and that we have thresholds set for when to turn them on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if __name__ == '__main__':
    yellow_led = LED(2)
    red_led = LED(3)

    warning_threshold = 0.02
    danger_threshold = 0.05
    seconds_in_day = 86400

    yellow_led.off()
    red_led.off()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We set the LEDs up on GPIO pins 2 and 3 and set our yellow and red light thresholds to 0.02 and 0.05. We've also set up the variable &lt;code&gt;seconds_in_day&lt;/code&gt; - we're going to use this as our polling rate. In other words, we're going to check the COVID data status once every day. This is the most sensible thing to do, since the COVID data from the COVID tracking API updates once every day at 4PM EST. Let's make our polling loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while True:
    p_rate = get_data()

    print("Positivity rate: %f" % p_rate)

    sleep(seconds_in_day)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will execute forever outputting the COVID positivity rate once per day. Now let's make the LEDs operate on that data. Put this in between the print statement and the sleep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if p_rate &amp;gt; danger_threshold:
    yellow_led.off()
    red_led.on()
elif p_rate &amp;gt; warning_threshold:
    red_led.off()
    yellow_led.on()
else:
    red_led.off()
    yellow_led.off()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns the yellow LED on if we've passed the warning threshold and turns the red LED on if we've passed the danger threshold. Only one of the LEDs will be lit at a time. If we're beneath both thresholds, neither LED will be lit.&lt;/p&gt;

&lt;p&gt;Now all that's left is to wire up the LEDs. Let's set up the circuits.&lt;/p&gt;

&lt;p&gt;LEDs are basic electronic components called diodes that only pass current in one direction. LED stands for Light Emitting Diode - they just happen to be a type of diode that makes visible light when they pass current. In order to hook up your diode in the correct direction (i.e., with the correct polarity) so that it actually makes light, you need to know which side of it is positive and which side is negative. For a through-hole LED, the long leg of the LED is the positive side, and the short leg is the negative (they're called through-hole because when they're installed in a circuit board, the legs go &lt;em&gt;through the holes&lt;/em&gt; in the board).&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%2Fi%2Fd75wo0tpq5zajc3tehrb.jpeg" 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%2Fi%2Fd75wo0tpq5zajc3tehrb.jpeg" alt="LED positive and negative terminals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll install the LEDs into a breadboard for convenience. If you decide to make this project permanent, you can think about soldering the circuit together and encasing it all in an enclosure (maybe in something that will diffuse the light! LEDs can light up a pretty big space if they're properly diffused). Set up your breadboard like this:&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%2Fi%2Fk7eaqcs2ocgi2h5ad16r.jpeg" 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%2Fi%2Fk7eaqcs2ocgi2h5ad16r.jpeg" alt="Breadboard circuit"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;If you're new to breadboards, a key thing to understand is that each row of the breadboard is continuous - you can think of it as a wire that you get to plug electronic components into. These rows end just before the two vertical rails on either side, which are notionally for power (red) and ground (blue). The vertical rails are separate but also each continuous.&lt;/p&gt;

&lt;p&gt;What we're doing is taking power from the positive rail of the breadboard, feeding it through a resistor, then to the LED, and then out to the negative rail of the breadboard. Note that each LED is powered by a separate power rail - this is important later! We need them to be separate so that we can control them each separately. The resistor acts as a current limiter for the LED, providing some assurance that the LED won't receive so much current that it burns up. In practice, a Pi's GPIO pins are (I believe) internally limited, so this isn't really required. But it's good practice any time you use LEDs or sensitive electronics. If you're curious how to choose the right resistor for the job, check out &lt;a href="https://en.wikipedia.org/wiki/Ohm%27s_law" rel="noopener noreferrer"&gt;ohm's law&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that the breadboard is set up, we need some way to power it. Since we want to be able to control when the LEDs get power, we're going to hook the positive rails of the breadboard up to a couple of the Pi's GPIO pins. These GPIO pins can be turned on and off in software, making them ideal for this and many other electronics hacking projects. We're going to use pins 2 and 3, since that's what we wrote in our python code.&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%2Fi%2Fakpxelisnzmw41brepca.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%2Fi%2Fakpxelisnzmw41brepca.png" alt="Raspberry Pi pinout diagram"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Credit to pinout.xyz&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since the Pi pins aren't labelled, you should look at &lt;a href="https://pinout.xyz/" rel="noopener noreferrer"&gt;pinout.xyz&lt;/a&gt; to help you get oriented and find the right pins. Once you do, connect pin 2 to the positive rail feeding the yellow LED, and pin 3 to the positive rail feeding the red LED. Connect a ground pin to the ground you're using on the breadboard.&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%2Fi%2F86od5hggqb2csxm8yrz6.jpeg" 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%2Fi%2F86od5hggqb2csxm8yrz6.jpeg" alt="The finished circuit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now run the code! If you've done everything right, your LEDs should turn off when the code first starts, and once the COVID positivity rate has been calculated, you should see the appropriate LED light up (if any). If you'd like, you can connect a third, green LED, that will indicate that the positivity rate is below 2%. That's left as an exercise to the reader.&lt;/p&gt;

</description>
      <category>electronics</category>
      <category>raspberrypi</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>OpenHAB Home Automation</title>
      <dc:creator>Dizzyspiral</dc:creator>
      <pubDate>Sun, 26 Apr 2020 00:54:18 +0000</pubDate>
      <link>https://dev.to/dizzyspi/openhab-home-automation-1c70</link>
      <guid>https://dev.to/dizzyspi/openhab-home-automation-1c70</guid>
      <description>&lt;p&gt;&lt;em&gt;This is part 1 in a series of posts documenting what worked and what did not while building a privacy and security focused smart home&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;IoT and the smart home realm of electronics is largely a joke in the security industry. Smart locks and garage door openers provide little to no physical security against a hacker or someone with one of their tools [&lt;a href="https://techcrunch.com/2019/07/02/smart-home-hub-flaws-unlock-doors/"&gt;1&lt;/a&gt;] [&lt;a href="https://blog.rapid7.com/2019/08/01/r7-2019-18-multiple-hickory-smart-lock-vulnerabilities/"&gt;2&lt;/a&gt;] [&lt;a href="https://www.cnet.com/news/smart-lock-has-a-security-vulnerability-that-leaves-homes-open-for-attacks/"&gt;3&lt;/a&gt;]. Smart bulbs and other "innocuous" IoT devices can be used for infection and propagation of malware, and en masse can pose a risk as botnets [&lt;a href="https://www.csoonline.com/article/3168763/university-attacked-by-its-own-vending-machines-smart-light-bulbs-and-5-000-iot-devices.html"&gt;4&lt;/a&gt;]. Cameras spy on citizens and provide intel to foreign (or sometimes domestic) governments[&lt;a href="https://www.reddit.com/r/wyzecam/comments/9zioog/do_these_still_spy_on_you_was_gonna_get_one_on/"&gt;5&lt;/a&gt;]. The point is, IoT devices are either wilfully or negligently full of security flaws that punch holes in your network and let attackers in or leak your personal data out.&lt;/p&gt;

&lt;p&gt;...So why on Earth am I building myself a smart home? Because I want this:&lt;/p&gt;

&lt;p&gt;"Hey Mycroft, turn on my kitchen lights."&lt;br&gt;
"Right away."&lt;br&gt;
&lt;em&gt;Four light fixtures turn on at once&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;How freaking cool? We're living in the future, folks.&lt;/p&gt;

&lt;p&gt;But how do I do this without compromising my privacy, or my network security? I've done a lot of research and I think I've found a decent compromise. Leaning heavily on open source software, I'm building a home automation network with &lt;a href="https://www.openhab.org/"&gt;OpenHAB&lt;/a&gt;, &lt;a href="https://www.z-wave.com/"&gt;Z-wave&lt;/a&gt;, and &lt;a href="https://mycroft.ai/"&gt;Mycroft&lt;/a&gt;. Let me explain why this is a good solution.&lt;/p&gt;

&lt;p&gt;Z-wave is a mesh network protocol that creates a "LAN" of IoT devices that are, themselves, never connected to the internet. These mesh IoT devices connect to a Z-wave "hub" that &lt;em&gt;can&lt;/em&gt; be connected to the internet, if you want it to (e.g. if you want to control your smart home from oot and aboot). The hub sends commands to the devices, updates the devices, and overall coordinates the smart home show. There are plenty of Z-wave hubs already out there (&lt;em&gt;cough&lt;/em&gt; SmartThings &lt;em&gt;cough&lt;/em&gt;), but I wanted something less opaque that would be amenable to the addition of some non-z-wave devices, such as the Wyze cam. Something more... open source.&lt;/p&gt;

&lt;p&gt;OpenHAB is a powerful smart home automation suite that frankly has impressed the socks off of me. This thing was clearly architected by people who knew what they were doing from a design standpoint, because their concepts (bindings, things, channels, items) generalize so well that once you understand the OpenHAB ecosystem, it's a joy to add and control a new device. &lt;em&gt;It just works&lt;/em&gt;. I love open source success stories. OpenHAB has a binding for Z-wave that allows it to act as a hub as long as there's a Z-wave serial device present on the system. It also has bindings for all sorts of other things you might want to integrate with your smart home solution, including for example Spotify. The beauty of a single-stop solution like OpenHAB is that you can gather all your state information in one place, and use it to create complex interactions between your home devices.&lt;/p&gt;

&lt;p&gt;While OpenHAB even provides a means for creating custom UIs for controlling your devices, sometimes talking to your house is just more natural (or more awesome). But voice assistants are all cloud-based services because the hard work of lexing human speech and deriving meaning is the work of an AI, and sadly, they seem to require more horsepower than a raspberry pi. Even more sad, though, is that these cloud-based services snarf up your data and sell it to advertisers. There's no guarantee that they aren't always listening. an indeed there's some evidence that they are, and that our personal moments are being recorded and transcribed by humans in order to improve the AI's speech recognition (I read this article, but I can't find it now - guess you will have to take my word for it).&lt;/p&gt;

&lt;p&gt;Mycroft is a cloud-based AI service, but it is open source, to the degree that it is possible. It requires an online account in order to link your Mycroft instance to its cloud service, and their privacy policy does contain language that enables them to sell your data to advertisers. They swear that they don't - being privacy conscious and respectful of their users is their biggest selling point and something that they tout on their website - but they absolutely could. It's up to the end user if they believe them. I decided they were the lesser of all the evils, and I'm excited about the potential to write my own skills for Mycroft, as its skills engine is python-based. It also integrates with OpenHAB, giving it access to all of the items exposed through OpenHAB's configuration. In theory, one can create Mycroft commands to control literally any of the smart home things.&lt;/p&gt;

&lt;p&gt;Here's the shopping list so far:&lt;/p&gt;

&lt;p&gt;$100 &lt;a href="https://smile.amazon.com/gp/product/B07V5JTMV9/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&amp;amp;psc=1"&gt;Raspberry Pi 4 Kit from CanaKit&lt;/a&gt;&lt;br&gt;
$39 &lt;a href="https://smile.amazon.com/gp/product/B07GNZ56BK/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&amp;amp;psc=1"&gt;Zooz Z-wave Plus 2 USB Stick&lt;/a&gt;&lt;br&gt;
$30 &lt;a href="https://smile.amazon.com/gp/product/B01A3SBTM6/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&amp;amp;psc=1"&gt;Zooz Z-wave Toggle Smart Switch&lt;/a&gt;&lt;br&gt;
$9 &lt;a href="https://www.walmart.com/ip/Sony-PlayStation-Eye-PS3/20470320"&gt;PS3 Eye Microphone&lt;/a&gt;&lt;br&gt;
$35 &lt;a href="https://www.adafruit.com/product/3775"&gt;Raspberry Pi 3 B+&lt;/a&gt;&lt;br&gt;
$25 x3 &lt;a href="https://smile.amazon.com/Raspberry-Pi-Model-A-256MB/dp/B00PEX05TO"&gt;Raspberry Pi 3 A+&lt;/a&gt;&lt;br&gt;
$25 &lt;a href="https://smile.amazon.com/Wyze-Indoor-Wireless-Detection-Assistant/dp/B076H3SRXG"&gt;Wyze Cam V2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That all runs a bit over $300. Expensive, sure, but what's life without a hobby. What all this buys me so far are three sets of networked and synchronized speakers (hooked up to Pi 3 A+'s running &lt;a href="https://www.max2play.com/en/"&gt;Max2Play&lt;/a&gt;), a voice assistant (the PS3 eye attached to the Pi 3 B+ running Mycroft.ai, using their &lt;a href="https://mycroft-ai.gitbook.io/docs/using-mycroft-ai/get-mycroft/picroft"&gt;picroft&lt;/a&gt; image), a solitary smart light switch, a camera to watch my front door, and a home automation hub (the Pi 4 with the Zooz Z-wave USB stick). So... I can turn on a single light, play some music, detect when someone's coming into the house, and ask the internet some questions. It's not bad but it's not exactly like the house is &lt;em&gt;alive&lt;/em&gt;. All I've bought so far is proof of concept toys.&lt;/p&gt;

&lt;p&gt;I'll continue this series with posts about each individual node in the smart home network, including any relevant configuration, wiring, networking, travails, and triumphs. For now, I wait for the pieces to come in.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>privacy</category>
      <category>security</category>
      <category>iot</category>
    </item>
  </channel>
</rss>
