Ben_Lolo on 10:14 PM 08/18/2021: For only being a 1.0 difficulty, I am absolutely stumped! Somewhat spoiler-y comment...
I found a function call just before strcmp(input, "qwerty"). In that function there is a second function being called multiple times. No matter what I enter, the values passed to that function second are always the same, resulting in the "bad" message being printed. I can only get a "good" message with patching. Any tips on finding the right password?
ali0gamer on 9:06 AM 08/19/2021: @Ben_Lolo the right password seems to be "faster".
Piggy63 on 3:34 AM 08/20/2021: @ali0gamer: Correct. How long did it take you to crack this?
@Ben_Lolo: This is a very simple crackme, as you only need to know simple arithmetic operations (easier than primary school math).
Using Hex-Rays Decompiler, we first find decompile the main function:
int __cdecl main(int argc, const char **argv, const char **envp)
{
printf("Password: ");
scanf("%s", &input);
sub_401490();
if ( !strcmp("qwerty", &input) )
printf("Correct");
else
printf("Incorrect\nHint: decompile this executable with Hex-Rays Decompiler...\n");
return 0;
}
As you can see, it compares the user input with "qwerty", but "qwerty" isn't the correct password. This means the function sub_401490 is responsible for checking the user input, and the string comparison in the main function simply acts as a distractor.
We then decompile sub_401490 to obtain this:
int sub_401490()
{
char v1[280]; // [esp-118h] [ebp-46Ch] BYREF
char v2[280]; // [esp+8h] [ebp-34Ch] BYREF
char v3[280]; // [esp+120h] [ebp-234h] BYREF
int v4[70]; // [esp+238h] [ebp-11Ch] BYREF
dword_4203A8 = 0;
qmemcpy(v1, v3, sizeof(v1));
qmemcpy(v4, sub_401040((int *)v2), sizeof(v4));
if ( sub_4015A0(v4[41], dword_4203A8)
&& sub_4015A0(v4[0], dword_4203A8)
&& sub_4015A0(v4[55], dword_4203A8)
&& sub_4015A0(v4[40], dword_4203A8)
&& sub_4015A0(v4[69], dword_4203A8)
&& sub_4015A0(v4[1], dword_4203A8) )
{
sub_4012D0();
}
sub_401440();
return sub_401460();
}
We can see some very long char arrays, which are most likely custom structures (they are all 280 characters long, so most likely the same structure), and sub_401040 takes that structure. That structure might be something used for containing the decrypted password. If that is the case, then sub_401040 is the function responsible for the decryption. We decompile that:
int *__cdecl sub_401040(int *a1)
{
int v2; // [esp+14h] [ebp-12Ch]
char v3; // [esp+1Ch] [ebp-124h]
char v4; // [esp+20h] [ebp-120h]
int v5[70]; // [esp+24h] [ebp-11Ch] BYREF
int v6; // [esp+150h] [ebp+10h]
v6 = dword_41F010 * (dword_41F000 / (dword_41F004 + (dword_41F008 dword_41F020) / (dword_41F02C dword_41F034);
STACK[0x260] = dword_41F01C
/ (dword_41F028 + dword_41F020)
* ((dword_41F000 dword_41F014)
/ (dword_41F010 (dword_41F00C + dword_41F008))
* (dword_41F018 v4);
v5[55] = dword_41F02C + v5[40];
v5[0] = v2;
v5[41] = dword_41F004 - (dword_41F010
ali0gamer on 5:14 AM 08/20/2021: @Piggy63 I saw a function calculating some characters and comparing them with the input by passing each character in reverse order to a function. i took a guess and wrote down the characters: retsaf. but before this i had found out that each time we call the char checking function we also pass it an index. as ghidra shows the return value of that function(Bool) gets concatenated with a empty string and then checked if its still an empty string if it was then this process gets repeated for each character until a function call that replaces failure message with "Correct".
ali0gamer on 5:21 AM 08/20/2021: Oh i made a mistake :/ the function checks if the empty string variable is not empty anymore ("\x00" = "\x00\x01"). if the bool value is 1 (success) then it proceeds.
ali0gamer on 5:38 AM 08/20/2021: I wish i could edit my comments :/ i just found out that CONCAT function from ghidra is something else it just concatenates bytes.
Ben_Lolo on 10:08 PM 08/20/2021: @Piggy63 Thank you for the extremely detailed walkthrough! Ghidra doesn't seem to decompile the functions quite as well as Hex-Rays does (I opted to use Ghidra because it's free). It's too bad someone spoiled the answer outright in the comments, because I would have liked to been able to figure it out with the functions you gave
Piggy63 on 12:05 AM 08/21/2021: @Ben_Lolo: You're welcome. I will make more simple crackmes like this in the future. Actually, I just noticed that my previous comment probably exceeded the maximum length, so parts of it got cut off. The lines that got cut off:
v5[0] = v2;
v5[41] = dword_41F004 - (dword_41F010
Piggy63 on 12:08 AM 08/21/2021: Ok, got cut off again at the exact same place. Here is my full comment:
https://pastebin.com/raw/nX7pNH58
anhnt on 5:18 AM 12/14/2021: I was a new person
start reverse engine. can you give me the source code of this crackmes so i can study and refer? Thank you very much.
reneeToto on 3:55 AM 03/09/2022: Hey Piggy, what anti-debug tricks did you put in this? When I manually disable isdebbugerpresent, while stepping through the program debugging it still kicks me out (in IDA).
Ghost57 on 3:40 PM 06/23/2022: SOLUTION :
int __cdecl main(int argc, const char** argv, const char** envp)
{
const char Str1[] = "qwerty";
const char Str2[] = "input";
printf("Password: ");
scanf("%s", &Str2);
//sub_401490();
if (!strcmp(Str1, Str2))
printf("Correct");
else
printf("Incorrect\nHint: decompile this executable with Hex-Rays Decompiler...\n");
return 0;
}
ten-ten on 9:22 PM 07/10/2022: Nice challenge, indeed the strcmp is to distract the players from the real flag