Silicon Valley Encryption, Part III

My attempt to de-obfuscate the easter egg from the HBO series Silicon Valley didn’t help in my efforts to reverse engineer the code. I got close, but I just can’t obtain that first (or final) value.

My focus, as covered in last week’s Lesson, is this expression from the original code:

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

This manipulation isn’t based on a loop or a changing value, so the result is consistent throughout the code. Yet, instead of setting a specific value, the above expression buries it with a bit shift operation that I can’t get any programmer’s calculator to output. If I output it in the code, I get 0x79481E6BBCC01223, which is the original value — not helpful. So this expression could just be a rouse.

Regardless, based on the code from last week’s Lesson, here is my attempt to reverse engineer the encryption:

2025_04_26-Lesson.c

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

typedef __int128_t bignum;

bignum iface(char c, int i)
{
    bignum r;

    r = ((c-65) | ((bignum)0x1FULL >> i * 5)) << i * 5;
    /* original expression
    c = (((s & ((dcf_t)0x1FULL << i * 5)) >> i * 5 ) + 65);
       */
    return r;
}

int main()
{
    char buffer[18];
    int i;
    bignum target,modifier,result;

    fgets(buffer,18,stdin);

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

    result = 0;
    for ( i=17; i>0; i-- )
    {
        result += iface(buffer[i], i) + modifier;
    }

    printf("%llX\n",(unsigned long long)target);
    printf("%llX\n",(unsigned long long)result);

    return 0;
}

The main() function prompts for a string to input. It sends each character to the iface() function, where the character is manipulated into a value. This value is returned and used to build the result variable in the main() function.

Once the loop has processed the string, the new value is output along with the original code’s value. They should match, providing that the exact string is input and my decryption method is sound.

Here’s a sample run:

DREAM_ON_ASSHOLES
79481E6BBCC01223
79481E6BBCC01220

No prompt appears, so I type the text DREAM_ON_ASSHOLES. You see the result. The values almost match, off by three.

Another way to test the code is to use the new value generated, 0x79481E6BBCC01220, and set it into the original code to run it backwards. Here is the original programs output when used with the new value:

AREAM_ON_ASSHOLES

The first letter ‘A’ is off by three, which follows. But where does the three come from? I even tried processing the string backwards, and the first letter was still ‘A’.

Then I tried using an alphabet string, ABCDEFGHIJKLMNOPQ. This string converted into the value 0xC5A928398A418823. When put into the original code, the output is ABCDEFGHIJKLMOLES. Weirdly, it retains the last part of the original string, “OLES.”

I’m at a loss.

Obviously, whoever coded this encryption method is quite clever not only at the process but at obfuscation. Perhaps someone else can look at what’s going on and see what I’m missing or offer some insight as to how the program works and which method was used to originally encrypt the string — if it’s indeed being encrypted and not just disguised.

7 thoughts on “Silicon Valley Encryption, Part III

  1. If 0x79481E6BBCC01223 + ((dcf_t)0x1222DC << 64) = 0x79481E6BBCC01223
    then ((dcf_t)0x1222DC << 64) must either evaluate to or be interpreted as 0.

    I have a theory:

    Hexadecimal 0x79481E6BBCC01223
    = binary 111100101001000000111100110101110111100110000000001001000100011
    (according to https://www.rapidtables.com/convert/number/hex-to-decimal.html) which is 63 bits, therefore will be a 64 bit int.

    The expression ((dcf_t)0x1222DC << 64) first converts 0x1222DC to 128 bit and then shifts all the bits to the left or highest half. The right or lowest 64 bits are therefore all 0.

    If you then add this to the 64 bit 0x79481E6BBCC01223 the left 64 bits which include 1s is effectively non-existent to a 64 bit number, so only the right 64 bits, all 0s, are added.

    Therefore:

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

    = 0x79481E6BBCC01223 + 0

    = 0x79481E6BBCC01223

    If this is true you could use any value instead of 0x1222DC (as long as it fits in 64 bits) and it would still work, as it'll still be shifted out of sight.

  2. I’ve done a bit of experimenting and confirmed that in
    modifier = (bignum)0x1222DC << 64;
    the cast is done first, then the shifts, as I'd expect. However, modifier still ends up with the value 0 even though there's room for the bits to be shifted into. The compiler seems to interpret the hard-coded 0x1222DC as a 32 bit type, but as I mentioned it's cast to 128 bits before the bitshift.

    There's either something really weird going on, or something really obvious which I missed.

  3. @Chris Webb
    “However, modifier still ends up with the value 0 even though thereʼs room for the bits to be shifted into.”

    With the info you provided in the above posts I canʼt be sure, but I would wager you tried to print the value of ‘modifier’ by relying on the %llu format specifier of the (f)printf() family of functions?

    I say this because the above does work exactly like you thought it would—i.e. after __int128_t modifier = (__int128_t)0x1222DC << 64; the variable modifier contains the value 1222DC0000000000000000₁₆. However, while one can perform arithmetic calculations⁽¹⁾ on such quantities when using GCC or Clang, the C standard library does—as of yet—come with no support for 128-bit integer values, they canʼt even be printed out—thatʼs why I wrote such functions myself:

    int fprintf_ui128 (FILE *restrict stream, __uint128_t ui128);
    int fprintf_i128 (FILE *restrict stream, __int128_t i128);

    As such the variable modifier isnʼt a modifier at all, but just the higher-order quadword of a 128-bit value. Because integer promotion rules still apply, one can then add the value for the lower 64-bits… just like shown in the first part of this series:

    __int128_t s = 0x79481E6BBCC01223 + ((__int128_t)0x1222DC << 64);
    /* after this assignment, s == 1222DC79481E6BBCC01223 */

    I have also written a program to perform the necessary steps to encode arbitrary text messages into 128-bit values, that can then be fed to _ctx_iface() to print them out again. Regrettably, I made a mistake and posted this code under part 1 of this series (instead of here):

    __int128_t encode_message (char const str [], size_t *char_count);

    Hope that clears everything up!

    ⁽¹⁾ Note, however, that both compilers come without any support for 128-bit integer literals. For this reason, the given ‘s’ value has to be constructed as shown in the original code.

  4. Here is a solution that was emailed to me from Chris H:

    #include <stdio.h>
    
    
    typedef __int128 bignum;
    
    void iface(bignum s, int i) 
    {
        bignum w = (bignum)0x1F << i * 5; //creates a 5 bit(1F = 11111) mask that is left shifted by i * 5
        bignum x = (s & w) >> i * 5;      //ANDS the 5 bit mask with bignum and shifts it right by i * 5
        int c = x + 65;		              //adds 65 to the 5 bit bcd value to get each letter
    	printf("%c",c);			      //the ULL means unsigned long long and the compiler ignores it
    }
    
    int main()
    {
    
        for (int i=0; i<17; i++) 
        {
    	
            iface(0x79481E6BBCC01223 + ((bignum)0x1222DC << 64), i);   //shifts 1222DC to the front of a 128 bit int
                                                                       //and adds it to the front of 0x79481E6BBCC01223
        }								                               //so you end up with 0x1222DC79481E6BBCC01223
    								                                   
    }
    
    // if you convert 1222DC79481E6BBCC01223 to binary and break into 5 bits
    // you will have to pad the front msb with two zeros to have enough bits
    // then add 65 to the bcd of each 5 bits and you get your hidden message
  5. Here’s another one from Chris H, which outputs a different message:

    #include <stdio.h>
    
    typedef __uint128_t bignum;
    
    void iface(bignum s, int i)
    {
    	bignum w = (bignum)0x1F << i * 5;
    	bignum x = (s & w) >>i * 5;
    	int c = x + 65;
    	printf("%c",c);
    }
    
    int main() 
    {
    
    	for (int i=0; i<13; i++)
    	{
    		iface(0x22218CA0FD1717C2 + ((bignum)0x1 << 64), i);
    	}
    	printf("\n");
    }
  6. M.Stumpfl mea culpa. I did use printf llu which printed 0. However, Dan said that target + modifier = target which led me to believe modifier must be 0. ¯\_(ツ)_/¯

Leave a Reply