For this particular challenge, participants were given an x64 binary asking for a valid serial number.

If the serial number is wrong, they should see the image below.

This seems like those traditional crackmes. Let’s try to find the function that is checking users’ input. In order to find it, we should always look for suspicious strings if applicable.

Using Hopper, we found the following strings as indicated in the image below.

So let’s pick one of the “strings” and see where it was referenced.

Ok, let’s try and see whether we can find any GetDlgItemText/GetDlgItemTextW API calls. Ok, it seems like we got one at 140001464.

If we step debug it and follow the flow, we will come across a string length check at 1400014b3. Now we know that the input string must be 32 characters in length.

Stepping through again, at 140001500 we will encounter a check to ensure that the input characters must be 1, 2 or 3. This makes sure that the serial for this challenge is only comprised of 1, 2 and 3.

If we were to analyze the application at 140001750, we can see that there is an array with an initial capacity of [0, 13, 7]. However the maximum capacity of the “jugs” is [19, 13, 7] and the expected end state is having the array be [10, 10, 0]. You basically have three “jugs” with a size of 7, 13 and 19. The 7 and 13 size “jugs” are filled and the container with size 19 is empty. What you need is 10 in two of the containers (13 and 19).

Let’s re-write it in pseudo codes. Let’s assume that 3 “jugs” are under the array, M and the 3 jugs are a,b and c.

for (int i = 0; i < szSerial.Length; i += 2){
    a = convert_it_to_int(Substring_of_szSerial(i, 1));
    b = convert_it_to_int (Substring_of_szSerial (i + 1, 1));
    if (a != b){
        switch (b){
            case 1:
                c = 19;
                break;
            case 2:
                c = 13;
                break;
            case 3:
                c = 7;
                break;
        }
        if ((M[b] + M[a]) > c){
            M[a] += M[b] - c;
            L[b] = c;
        }else{
            M[b] += L[a];
            M[a] = 0;
        }
    }
}

At first it may seem to be very confusing what is actually going on here. But let’s take a closer look.

This is the limit of the mugs.

Limit: 19    13    7
-----------------------
             A     B     C
Jug:	1  | 2   | 3
Water:	0  | 13 | 7

If [B] +[A] > Limit-of-[A]
    Fill [B] and put the remaining in [A]
Else
    Fill [B] and clear [A]

Now in the serial, if we were to start with 31, it simply means to fill up jug C to jug A.

So the aim of this is to move around the “beer” so jug A == 10 and jug B == 10.

We would have realized that this is the classic “Liquid Pouring Puzzle” that some, if not most of us, have seen while we were in school.

You can write your tool based on the findings. But if we were to do it with pen and paper. We should get something like the one below.

0-13-7
7-13-0
19-1-0
12-1-7
12-8-0
5-8-7
5-13-2
18-0-2
18-2-0
11-2-7
11-9-0
4-9-7
4-13-3
17-0-3
17-3-0
10-3-7
10-10-0

Rewriting this to serial code: 31211332133221321332133221321332

Using that as the serial, we will solve it and get this message back.