Reverse engineering, Windows internals, x86 magic, low level programming, and everything else I feel like writing about.

CrySyS SecChallenge 2020: Faulty Companion

It’s a cold winter evening, and you’re sitting on your couch at home, bored. The weather is so bad that you can’t go out which makes you dejected. You can’t play any games or watch movies because the internet is out. Since you don’t have any better idea you turn to your trusty companion, Alexander. Alexander, tell me a joke!. You see the little blue ring go around and then an unholy noise comes out from the device and it shuts down. Oh, you piece of s... you begin to say, it worked offline last time you tried. After restarting the device, you say again. Alexander, tell me a joke!. It shuts down with the same noise. GREAT! JUST GREAT! It’s broken…

A few minute passes by and in your boredom an idea comes to your mind. What if you would look into the issue? Anything is better than sitting on the couch looking at the wall. Let’s do it!

You say it again. Alexander, tell me another joke!. The same happens. Maybe it’s a hardware issue? You take the cover off and look around to check what’s on the pcb, when it hits your eye. There are several chips labeled Random joke generator 2000. What the hell? Why would anyone do something like this? Several chips dedicated to just one functionality? Telling jokes? Really?

You look around and find two test points next to the main chip labeled TP_JOKES_SCL and TP_JOKES_SDA. Perhaps you should probe it. You take your oscilloscope and hold it to one of the points while you say it again. Alexander, tell me a joke!. Bingo. The signal begins to jump all around the place, until the weird noise. Something doesn’t seems right, you can feel it in your guts. You look for your logic analyzer but remember that you gave it to Joe two weeks ago. Ahh no… Your only other option is the knockoff one, which has no software support. Whatever, you sample the bus while saying the magic sentence.

Suddenly, the power goes out. Alexander powers off, and you’re running on your batteries. Luckily, the sampled values are already saved in a file. You have a few hours until your battery runs out. Will you be able to figure out what’s up with the joke generator chips? Perhaps one of them is faulty? Let’s find out!

Categories

Hardware, Logic analyzer

Files

Solution

The linked article explains how communications work on wire. In a nutshell, you only care about the value of the data wire when clock changes from 0 to 1, and each 8 bits that represent a byte are followed by some response bits. First we filter out the bitstream, then just try various steps and shifts to convert the bitstream into bytes, and look for a flag-looking thing with regex.

uint8_t bin_to_byte(const uint8_t* b)
{
  unsigned v = 0;
  for (auto i = 0u; i < 8; ++i)
  {
    v |= b[i];
    v <<= 1;
  }
  return (uint8_t)(v >> 1);
}

int main()
{
  const auto fp = fopen("samples.csv", "r");
  // throw away first line
  fscanf(fp, "Time[s], TP_JOKES_SCL, TP_JOKES_SDA");
  float time;
  int cl, da;
  auto last_cl = 1;
  std::vector<uint8_t> bits;
  while (3 == fscanf(fp, "%f, %d, %d", &time, &cl, &da))
  {
    if (last_cl == 0 && cl == 1)
      bits.push_back(da & 1);
    last_cl = cl;
  }
  fclose(fp);
  assert(bits.size() > 20);
  const static std::regex flag{"cd20\\{.{4,64}\\}"};
  std::smatch match;
  for (auto gap_bit = 0u; gap_bit < 4; ++gap_bit)
  {
    const auto step = 8 + gap_bit;
    for (auto i = 0u; i < step; ++i)
    {
      const auto data  = bits.data() + i; // skip i bits
      const auto count = (bits.size() - i) / step;
      std::string str;
      for (auto j = 0u; j < count; ++j)
        str.push_back(bin_to_byte(&data[j * step]));
      if (std::regex_search(str, match, flag))
      {
        printf("%s", match[0].str().c_str());
        break;
      }
    }
  }

  return 0;
}

Output:

cd20{REDACTED}