Silicon Valley Encryption, Part II

When Alexander faced the challenge of untangling the Gordian Knot, he just sliced it with his sword. Brilliant. Alas, untangling obfuscated C code takes more than a swift swipe of a blade.

From last week’s Lesson, I presented highly obfuscated code from the HBO series Silicon Valley. It appears in a scene from the show where programmers attempt to decipher the Pied Piper compression algorithm. They marvel at a number in the code and wonder what does it mean?

It means nothing, of course, as it’s an easter egg. If you run the code, it outputs the message DREAM_ON_ASSHOLES, just one of many cyber-pranks in the series.

Here is the original code from last week’s Lesson:

2025_04_12-Lesson.c

#include <stdio.h>
#include <stdlib.h>

typedef unsigned long u64;

/* Start here */
typedef void enc_cfg_t;
typedef int enc_cfg2_t;
typedef __int128_t dcf_t;

enc_cfg_t _ctx_iface(dcf_t s, enc_cfg2_t i){
    int c = (((s & ((dcf_t)0x1FULL << i * 5)) >> i * 5 ) + 65);
    printf("%c",c); }
    enc_cfg2_t main() {
    for (int i=0; i<17; i++){
        _ctx_iface(0x79481E6BBCC01223 + ((dcf_t)0x1222DC << 64), i);
    }
}
/* End here */

To make the code more readable, I first removed the typedef statements, restoring the original data types int and void.

The __int128_t typedef caught me off guard. In gcc and clang, __int128_t is a valid C language data type, representing a 128-bit integer value. It’s similar to a long long int, but not the same from what I gather. Regardless it’s non-standard. As a bonus, it’s delightfully cryptic, so I decided to typedef it myself and call it bignum.

I reformatted the rest of the code and broke apart some of the complex expressions. Here’s my result, which isn’t any more understandable, just a modicum more readable:

2025_04_19-Lesson.c

#include <stdio.h>
#include <stdlib.h>

typedef __int128_t bignum;

void iface(bignum s, int i)
{
    bignum w = (bignum)0x1FULL << i * 5;
    bignum x = (s & w) >> i * 5;
    int c = x + 65;

    printf("%c",c);
}

int main()
{
    int i;
    bignum hidden_value,modifier,result;

    /* hidden value is 17 digits long */
    hidden_value = 0x79481E6BBCC01223;
    modifier = (bignum)0x1222DC << 64;
    result = hidden_value + modifier;

    for ( i=0; i<17; i++ )
    {
        iface(result, i);
    }
}

It still bothers me that the main() function lacks a return statement, but that’s the least of this code’s puzzles.

The program parses through the value 0x79481E6BBCC01223 and outputs the string DREAM_ON_ASSHOLES. One of the keys is this statement:

int c = x + 65;

ASCII code 65 is the letter ‘A’. So whatever is happening with the original number, the code extracts values from zero through 25 representing letters of the uppercase alphabet, plus the underscore. The true puzzle is how it gets to this point.

One frustrating step along the way is this expression from the original code:

0x79481E6BBCC01223 + ((dcf_t)0x1222DC << 64)

The value 0x1222DC (a 128-bit integer) is shifted left 64 bits. When I ran this expression through a programmer’s calculator, the result was an overflow. The problem is that the actual value being manipulated, the code to decode, is the result of the above expression. This complexity further obfuscates the original value. It also frustrates me because I can’t find a programmer’s calculator that yields the actual value being manipulated.

My untangled code (above) generates the same output as the original. But true understanding requires that I reverse engineer the code. At some point in time, someone working on the show came up with the means to encrypt the string DREAM_ON_ASSHOLES and arrive at the value 0x79481E6BBCC01223. If I can repeat this process in reverse, it might help understand what’s really going on.

I make the attempt in next week’s Lesson.

8 thoughts on “Silicon Valley Encryption, Part II

  1. I agree that with this new ‘bignum’ typedef, the code—as it is now structured—is a lot easier to read. The given ‘(bignum)0x1FULL’ might still be puzzling though: what value (of what type) is a 0x1 hexadecimal literal with suffix FULL supposed to be? This FULL, however, is ​​only there to throw the reader off track. The code could be changed to bignum w = (bignum)0x1F << i * 5; and nothing would change… revealing that itʼs actually the value 0x1F this line of code is all about. (I hope I havenʼt given anything away of next weeks blog post with this remark.)

    One other thing I noticed with regards to “The __int128_t typedef caught me off guard […] It’s similar to a long long int”: I think this characterization is somewhat misleading. I guess itʼs true that a long long int could be redefined to specify a 128-bit integer type in the future (just like the meaning of int changed from specifying a 16-bit integer type to a 32-bit integer type when 32-bit CPUs began to dominate in the market). However, as it stands a long long int is still just a 64-bit type on Linux (and Windows).

    Sidenote: it’s unfortunate that the meaning of long differs between operating systems.

    Windows / Microsoft Visual C++:
    sizeof(long) == sizeof(int) == 4
    sizeof(long long) = 8

    Linux / GCC, Clang:
    sizeof(long) == sizeof(long long) == 8

  2. Back when I first learned to code, a short was the same as a char, an int was 16 bits, and a long was 32 bits. That the C language priests let these sizes fluctuate was a bad decision.

    The reason I wrote that the __int28_t thing is similar to a long long int is that the %llu placeholder works for it.

  3. I was quite busy today, so unfortunately I only saw your answer just now!

    «The reason I wrote that the __int28_t thing is similar to a long long int is that the %llu placeholder works for it.»

    Iʼm sorry, but I donʼt think thatʼs true… as the following program demonstrates:

    #include <stdio.h> // fprintf(), stdout
    #include <stdlib.h> // EXIT_SUCCESS

    __uint128_t const UINT128_MAX = (__uint128_t)(__int128_t)-1;
    __int128_t const INT128_MAX = UINT128_MAX >> 1;
    __int128_t const INT128_MIN = -INT128_MAX – 1;

    int main (void)
    {
      fprintf (stdout, "%llu\n", UINT128_MAX); /* 18446744073709551615 */
      fprintf (stdout, "%lld\n", INT128_MIN);  /* 0 */
      fprintf (stdout, "%lld\n", INT128_MAX);  /* -1 */

      return (EXIT_SUCCESS);
    }

    This will print out the values given as a comment.

    While GCC does support doing calculations with 128-bit (un)signed integers, the C standard library sadly doesnʼt really support any operations on them (as of yet).

  4. In order to print out (un)signed 128-bit integer values, it is—sadly—necessary to convert them to a string (which can then be printed using a simple %s format specifier).

    If I havenʼt made any mistakes, printing out arbitary 128-bit integers should be possible by relying on the following two helper functions:

    int fprintf_ui128 (FILE *restrict stream, __uint128_t ui128)
    {
      if (ui128 == 0)
        return (fprintf (stream, "0"));

      { char str [sizeof("340282366920938463463374607431768211455")]; /* 2¹²⁸-1 */
        size_t digit_index = sizeof(str);

        str [–digit_index] = '\0';

        while (ui128 > 0)
        {
          str [–digit_index] = '0' + (ui128 % 10);
          ui128 /= 10;
        }

        return (fprintf (stream, "%s", str + digit_index));
      }
    }

    int fprintf_i128 (FILE *restrict stream, __int128_t i128)
    {
      if (i128 == 0)
        return (fprintf (stream, "0"));
      else
      if (i128 == INT128_MIN)
        return (fprintf (stream, "-170141183460469231731687303715884105728"));

      if (i128 < 0)
      {
        fputc ('-', stream);
        i128 = -i128;
      }

      return (fprintf_ui128 (stream, (__uint128_t)i128));
    }

    With these functions, the following calls will print out the correct values:

      fprintf_ui128 (stdout, UINT128_MAX); fputc ('\n', stdout);
      fprintf_i128 (stdout, INT128_MIN); fputc ('\n', stdout);
      fprintf_i128 (stdout, INT128_MAX); fputc ('\n', stdout);

    I.e. these calls will result in the following output:

    340282366920938463463374607431768211455
    -170141183460469231731687303715884105728
    170141183460469231731687303715884105727

  5. A small addition to the last post: finding out the individual digits by repeatedly dividing by 10 is relatively slow.

    Because log₂(10) ≈ 3.322, thereʼs 19 decimal digits that fit within a 64-bit value (taking up ~63.116 bits). The next decimal digit is straddling the 64-bit border—about 0.883 bits can be found in the lower 64-bit value, the remaining 2.438 bits are encoded in the upper 64-bit value, and the remaining 61.561 bits encode another 18 decimal digits (and some change).

    int fprintf_ui128 (FILE *restrict stream, __uint128_t ui128)
    { static unsigned long long lower_order_div = 10000000000000000000ull;

    if (ui128 <= ULLONG_MAX)
        return (fprintf (stream, "%llu", (unsigned long long)ui128));

    { unsigned long long lower_order_digits, higher_order_digits;
        int straddling_digit;

        lower_order_digits = ui128 % lower_order_div;
        ui128               = ui128 / lower_order_div;
        straddling_digit    = ui128 % 10;
        higher_order_digits = ui128 / 10;

        return (fprintf (stream, "%llu%d%llu",
                         higher_order_digits, straddling_digit, lower_order_digits));
    }
    }

    With this implementation, it takes about 9,863 s to print out the above UINT128_MAX value 100,000,000 times.

    For comparison: performing the same task by repeatedly dividing by 10 takes about 19,347 s on my computer… i.e. this implementation is about twice as fast as the one I posted yesterday.

  6. I canʼt answer as to why GCC ships with rudimentary support for 128-bit integers, while glibc doesnʼt even offer functionality to print out such values.

    While todays processors offer no direct instructions to perform basic arithmetic on 128-bit quantities (not with SSE, not even if AVX is available), itʼs certainly not too hard to implement such operations by working on 64-bit at a time (as GCCʼs __int128 and especially big number libraries like GMP and MPFR prove).

    I guess the main hindrance is, that __int128 is a compiler-specific extension; if glibc support for such types were added, that would open up a whole can of worms, because such support would have to be added all over the library code… and what—non-standard—format specifier (for the printf() and scanf() family of functions) would/should the developers of glibc choose?

    As long as the standard stays silent on the possibility of native integers with a size larger than 64-bit, the situation is unlikely to improve. (No, not even C23ʼs _BitInt() provide the answer… yet; even there the reality is, that as of now there is no standardized way to read in/print out such values.)

    The only solution—for now—that I can think of, would be to rely on printf hooks to register a custom format specifier (for which one of the above functions would then be called).
    Of course, as glibc offers printf

  7. hello @ M.Stumpfl,
    if allowed, I’d ask you to have a look in “libint128”, https://gitlab.gnome.org/newbie-02/libint128, it ( tries to ) solve the I/O problem for 128-bit integers, while not yet for BigInt( x ). It’s not a printf solution but more like snprintf into string, however in two step ( e.g. i128toascd – printf the string ) also provides normal print. Supported are hex, decimal, octal and binary ASCII to (u)int128, and vv.

Leave a Reply