Introduction

Hello and welcome back! In this blog post we will solve a fairly simple reverse engineering challenge that exposes us to the dreaded SIMD instructions. These are instructions used in floating point operations among other things. Luckily this challenge wasn't too difficult even with these crazy instructions. If you prefer to watch YouTube videos you can check out my corresponding walkthrough below.

If you want to follow along you can download my VM. The binary will be in the `/home/kali/reverse_engineering/crackmes/sh4ll2` directory. Of course you don't have to use my VM, but you will need the binary. You can download that here. It comes in a password protected zip file. The password is "crackmes.one" without the quotes. You might also want a disassembler. I suggest IDA or Ghidra. I'll be using Ghidra throughout this tutorial. With all of that out of the way let's get reversing!

Initial Triage

Let's start out with running `file` on the binary.

The binary is 64-bits, dynamically linked, and not stripped. We always like seeing binaries that aren't stripped so let's go ahead and look at the symbols with the `nm` command.

There isn't much going on here. A lot of this stuff is standard C stuff. We do see the main function but that's not too interesting, every C/C++ program has a main function lol. So, the last step is to run `strings`.

Again nothing too interesting. We see the "Good password" and "Bad password" strings which appear to be the only output the program gives. Now, without further ado, let's open the binary in Ghidra!

Static Analysis in Ghidra

We see the normal function prolog, but the instruction highlighted in red is a special instruction. Additionally, the register used `XMM0` is a special register. The `XMMn` (where n ranges from `0-31`) are used to handle floating point numbers. They have other uses as well, but we are primarily concerned with this particular use case. We see the highlighted instruction takes a value from memory and stores it in the `XMM0` register. We can see this value is `0x40641893`. While this looks like a huge number, we have to keep in mind that this is going into a floating point register. We can tell Ghidra to interpret this has a hex value. First, double-click on the `DAT_001008B4`.

We see that Ghidra labels this as undefined and therefore the hex value stored there are interpreted as simply hex. Since we know this is going to be a floating point number, we can change the type to float. Right-click on the variable and go to Data and select float as depicted in the image below.

After you do that you'll notice the value is updated to reflect the floating point value.

Great! We changed the data type and now we know the value that gets stored in the `XMM0` register. Let's go back and continue our analysis.

Ok we see that `3.564` gets stored in the `local_14` variable. In usual Jaybailey216 fashion, let's rename this variable to `is_3.564`. We then see, another variable `local_1c` is being passed to `scanf` along with what looks like is a format specifier. Let's double click on `DAT_00100894` to see what format specifier is being specified lol.

We see that the format specifier is `%d` so `local_1c` is going to hold a decimal number that is NOT a floating point number. Keep this in mind! Additionally, let's rename `local_1c` to `user_input`.

Alright, after we grab the user input, we store that in the `EAX` register. We then see another instruction we've never seen before, but you might be able to guess what it's doing. `PXOR` will, as you might have guessed, `XOR` two `XMMN` registers. The instruction after that is also foreign and quite frankly pretty gross! `CVTSI2SS` converts a non-floating point number to a floating point number. This instruction stands for convert signed integer to scalar single precision floating point value. I know that's a mouthful! So, if we typed in the number 5, this will convert that to a floating point number (5.0) and store that in the `XMM0` register. We then see the `ADDSS` instruction. This instruction seems pretty self-explanatory, it adds two floating point values. The result is stored in the `XMM0` register. So, this will add our user input and `3.564`. So, to keep track of this, `XMM0` now contains `8.564`, assuming we typed in 5. The next instruction `CVTTSS2SI`, does the exact opposite as the other instruction, it takes a floating point number and converts it to a signed integer. So, `EAX` will hold `8`. This result is then stored in `local_18`. Another thing to keep in mind, `local_18` is NOT a floating point number. Let's go ahead and rename `local_18` to `sum`. Finally, we see the `XMM0` registers are zeroed out again, then we see `local_10` being assigned to `XMM0` followed by an unconditional jump. Let's go ahead and rename `local_10` to `i`. Also, keep in mind that `i` is a floating point number and not an integer.

The above code is a `while` loop. We see that `i` is compared to `sum`. The `UCOMISS` instruction compares two single precision floating-point values. This instruction sets the `EFLAGS` register depending on the result of the comparison. If either of the operands is a `NaN`. This instruction sets the following flags: overflow `OF`, zero flag `ZF`, and parity flag `PF`. If the destination operand is greater than the source operand, none of the flags are set. This means, `ZF`, `OF`, and `PF` will all be zero. If the destination is less than the source, the `CF` register is set. If they are equal, the `ZF` flag is set. The `JA` instruction is jump if above, which checks the `CF` and `ZF` flags are not set. If they aren't set than we perform the jump. Now, let's take a look at the contents of the loop, starting at address `0x0010076F`. We see the `XMM0` register is zeroed out. The `sum` is placed in the `XMM0` register and we see `i` is added to `sum`. Recall, that `i` is a floating point number so this will preserve the precision. So, continuing our example where we input `5`, `sum` will be `8.564`, and after the `ADDSS` instruction, the `XMM0` register will hold `8.564`, because at this point `i` is still `0`. We then see see another variable, `local_c` is stored in the `XMM1` register and this is then added to `XMM0`. To put it simply, this is `local_c + sum + i`. We then see `XMM0` gets placed in the `local_c` variable. So, this is equivalent to: `local_c = local_c + sum + i`. Now we see another instruction involving a different `DAT` memory address. Let's go ahead and change the type just as we did before.

Now it's a little clearer what's going on. The variable `i` is going to be incremented by `0.8` in this loop. This explains why `i` is being used as a floating point number. I know that was quite a lot up there so let's go ahead and write out the equivalent C code.

``````while(sum > i)
{
num = sum + i + num;
i += .8
}``````

I renamed `local_c` to `num` but that is all the above code is doing.

Alright we're almost finished. First, the variable, `num` is passed to the `printf` function. We can't see what the format specifier is so let's use the same method as earlier to get the format specifier.

This time, we see a floating point format specifier being used so this is going to print out the floating point value. We then see that `num` is compared to another value that appears to be a floating point value. Let's convert this to a float so we can see the value `num` is being compared against.

Sweet, so, `num` is compared to `4550.8`. So, this means that we have to provide a number that when it goes through that loop, the sum will be equal to `4550.8`. Ok easy enough right? Well, I wasn't able to figure out a clever way get this value so I bruteforced it lol. I simply provided a few numbers until I eventually got the number I wanted. What's great is this binary shows you the out the result of the calculation so you can see how far or close you are to getting the magic value.

I eventually came up with 46 which satisfies the equation. I have no idea why 46 is the magic number I just know that 46 is the only number that works. If you know why 46 works I would love to know. Anyway you can see my decompiled code below. Hopefully this helps clear up any confusion in this post!

``````float num1_3.564 = 3.564
int user_input
scanf("%d", &user_input) // 5
int result
result = num1_3.564 + (float)user_input // 5.0 + 3.564 = 8
float i = 0
while(result > i)
{
num = result + i + num
i += .8
}
printf("%f", num)
if(num == 4550.8)
{
return 0;
}
return 0;``````

In this particular pseudo.c I annotated the variables with types to ensure we remember which variables are integers and which are floats.

Conclusion

Alright that's all there is for this challenge. This challenge was pretty simple but if you've never dealt with the special instructions and registers it can be a little confusing. Additionally, if you weren't aware of that little Ghidra trick, it can be a little misleading what the values of the registers and variables hold. I hope you all enjoyed this and learned something from this tutorial. If you have any questions feel free to hit me up on Twitter, Instagram, or Discord: jaybailey216#6540. If you have a challenge you want me to try next, let me know and I'll give it a shot!