00 - Defeating A Serial & Username Check
Here we will be reversing a binary that has 2 checks that validates if a username entered is correct and then the serial number entered, can we read the assembly and defeat/find both checks?
Purpose & Outcome
Many pieces of software try and implement anti cracking to protect their intellectual property and keep their software safe against pirating and or sold on black markets and abused etc. Today the binary we will reverse is fairly simple but showcases a small glimpse of checks some software providers use to protect the software, however we will try to defeat the username and serial check.
Source Code
It is important to note the functions called below in the source code of the binary we do not have their actual source code to read, so we are forced to read the assembly! The functions that we do not have the source code to are the validate_name and valid_serial, init_wargame functions.
Figuring Out A Valid Username
Below we go ahead and run the binary and quickly get met with "Enter your name:", the program is prompting us to enter the name and this could be a check that typically wants to see if of course the username you enter is valid. Usually pieces of software which are more mature will of course perform these validations server side, but you never know and many developers hard code and make mistakes on the client side! With that said lets go ahead and try to figure out the username.
Great so after doing some source code analysis and figuring out that we cant read the source of the function which validates this username check which is validate_name, we now must spin up the GDB debugger and get to doing some debugging and reading the functions assembly code!
Reversing The Assembly Of Validate_Name Function
I have seen longer lines of assembly and this function is pretty straight forward but just like any big task we need to piece it up. To begin with we will break down the functions assembly piece by piece and try and understand the bigger picture as we go! Here below is the first chunk:
What this first chunk of the assembly is doing is the following: It sets up the stack and then allocates 16 bytes, after doing so it then moves the value from within the RDI register into the offset of RBP-0x8 which holds a memory address and the value is then placed there. After doing so that same offset moves the value again into the RAX register. Great so now we have a decent idea of what we may need to figure out. Once again the value in the rax register is moved into the RSI register and then the value at the address 0x400cc8 is moved into the EDI register and then strcmp which is a function that compares strings is called. Think of strcmp as the following, if the strings compared are equal then 0 is returned and if not it returns 1. Great now after that, we called a test because the return value of most functions is usually stored in the RAX register but we test EAX which is the 32 bit aspect of the RAX register. Test performs a AND operation basically so if we had the strings not equal the EAX register would be 1 and then when it is 1, now the ZF flag is set to 0 which tells our CPU to basically not jump to the address specified if equal. However if the eax register is set to 0 and we test 0 AND 0 we then set the XF flag to 1 which now means the string comparison we did was equal and we can proceed to jumping to the address at 0x400a27. Great but to add one more thing when calling the strcmp function we are comparing the values in the RDI and RSI registers. So what does this conclude? We should go ahead and figure out what is in both of these registers maybe this is where the values of our input and a username is compared? Mhm.
Okay so to check if we theoretically our idea is correct we will set a breakpoint and the following addresses within to the program to stop the execution flow and dump the registers: 0x4009cf and then 0x4009da. Lets see if we are right here below is a small snippet of what is going on...............:
Great so now as we can see the RDI register at the start holds the value of the name we input at the console of the program (which was hello world this time) and then it moved it towards the end a little to the RSI register and moved the value stored at 0x400cc8 into EDI which is the 32 bit of the RDI register thus now our name input is stored at RSI and the value which is compared it to is the value which we dumped was "Bob". Hmmmm so can we now check and see maybe if "Bob" can get us passed the first check as a name? Lets check that out below and see if we are right or not:
NICE! Bob was a valid name, and if you check before you can see that upon entering a incorrect username we could not pass the functions check and the program got terminated and we failed :(, so we now know "Bob" is a valid username. But if you look closely at the assembly it also gives us information which you may be wondering about such as "Alice" and "Mallory" and more maybe we can pull out but we will focus on these now and see if maybe these can be valid usernames or not?
Okay great! We now know that we have 3 valid names we can use to pass the validate_name function and check which are "Bob, Alice, Mallory"! Now we need to enter the correct serial key can we find it? We do know that the function that checks the key is valid_key.
Extracting The Serial Key
So now that we know we have three different and valid usernames we need to enter the correct serial key and get that to work, how can we do so considering we have no access to that source codes function? The same way we did with the other function we are forced to be able to read the assembly and extract the key!!! Below is an image of the assembly code and commented each line!
Great so what we can see from this is the following, the serial key we entered is stored at RBP-0x2c and it is compared with the key which is in the EAX register and we will see if it is valid or not. We will set a breakpoint at the address 0x4009b7 and enter a random key inside to see! Here it is below:
Okay so we entered the following as the serial code which was "1234567" and then we set a breakpoint below where the comparison is being made at 0x4009b7 and we found out if you see the small video above that our input is stored at RBP-0x2c and then being compared against the value in the EAX register, mhmmmm so we dumped the eax register and the number we got was "134032" which may just be the serial code.... lets check and see if we are correct below!:
So our theory was correct, and we quickly defeated both the checks in the program!
Conclusion
So to conclude, this was a simple program with 2 checks asking for a correct username and a correct serial code number. Many software that has been developed has some types of checks as so above and although this was quite simple to defeat many do have checks of this sort and make the mistake of hardcoding values, usernames, and more in the program and we can take advantage of that easily. I will end this off by saying, THE ASSEMBLY NEVER LIES ALWAYS!
Last updated