This challenge is a .NET application. There are many tools to decompile a .NET application and here I use the ILSpy.
A quick look at the decompile result I found that this application is probably obfuscated by SmartAssembly :
There is a powerful tool named d4dot which can help with the deobfuscation. The tool is easy to use so I will not talk about more details here. After the deobfuscation the code looks much better and let’s go to the Entry Point function at first:
This function is easy to understand, and the most important logic here is to compare your input with a string stored in a variable named “b”. And the string “b” is made up with two parts that generated by two different functions Class3.smethod_0() and Class3.smethod_3(). The code of Class3.smethod_0() is shown as below:
This function does a simple XOR calculation on the array returned by function Class3.smethod_2() and the second arguments passed to this function. Here is the code of function Class3.smethod_2():
This function is interesting, it returns the byte code of method 100663297 as a byte array. So what is method 100663297? If you are not familiar with .NET file format, I recommend you to read the article here at first:
In short, the 100663297 (0x06000001) is a .NET token which can be split into two parts, the highest 8 bit (0x06) is an index for a MetaData table, and the rest 24 bits (0x000001) is the index of the item in that table. Following is a list of .NET MetaData tables:
The index 0x06 here represents the MethodDef table. We can use the CFF explorer to view the .NET tables. With this tool we can find that the first item (0x000001) in the MethodDef table is a function named .cctor, and we can also disassemble this function:
Now that we have the byte code of the function, and we also know the second augments passed to the Class3.smethod_0() (from the decompiled code), we can easily calculate the first part of the string “b”:
bytecode =(0x72, 0x01, 0x00, 0x00, 0x70, 0x26, 0x2A) arg2 = (31, 100, 116, 97, 0, 84, 69, 21, 115, 97, 109, 29, 79, 68, 21, 104, 115, 104, 21, 84, 78) bytecode_len = len(bytecode) out = '' i = 0 for b in arg2: out += chr(b ^ bytecode[i%bytecode_len]) i+= 1 print out
Ok, now we can move to the second part of string “b”, which is generated by function Class3.smethod_3():
This function also straightforward, it concatenates all the CustomAttributeData as a string and then calculates the MD5 hash of it. The CustomAttributeData can be easily read from the .NET decompiler:
However, this is a problem that we don’t know the correct order to put those CustomAttributeData together as a string. Since there are not too many CustomAttributeData, one possible way is to brute-force all the orders. But I will not solve this challenge in this way.
As we already have the first part of the string “b”, when this program compares the string with our input, the string must present in the memory, so why not just search it in the memory?
With this idea I launched this program in a debugger and let it run directly. It will ask us to enter a password:
We can input any password and continue executing the program until it exits. Now open the memory window and search for the first part of the string in Unicode format:
And finally we will get what we want in the memory:
Provide the correct password to this program we have the email address: