So far, looks like only @dtm has provided a solution the my little challenger (not really a surprise ) . He also gave us a couple of hints on how to find a solution. I have already given a couple of hints as well in the comments but, in case you havenāt manage to solve it and you are wondering how the hell this is done, keep readingā¦
Letās start with the binary with the symbols. This is the one I posted as a comment to the original crackme post and I will be referring to it as c2
. I will name the original one (without symbols) c1
.
Cracking the binary with symbols
The one with the symbols is easier, in the sense that debugging tools can parse some information in the file and provide a more human friendly view of the binary.
The output of objdump -d c2
will show us the different functions on the binary and will help us to understand what it does. Letās dump the asm into a file and open it with your preferred editor.
$ objdump -d c1 > c1.dump
$ vim c1.dump
Now, we can look for the main
function (press ESC
+ :/main
+ ENTER
). You will see some calls to different functions. Namely: my_print
, my_getpass
, obfuscate
and strncmp
. This is an excerpt of the interesting part:
400374: bf 35 0b 40 00 mov $0x400b35,%edi
400379: e8 44 ff ff ff callq 4002c2 <my_print>
40037e: 48 8b 0d 83 0c 20 00 mov 0x200c83(%rip),%rcx # 601008 <pass>
400385: 48 8b 45 f0 mov -0x10(%rbp),%rax
400389: ba 20 00 00 00 mov $0x20,%edx
40038e: 48 89 ce mov %rcx,%rsi
400391: 48 89 c7 mov %rax,%rdi
400394: e8 c4 05 00 00 callq 40095d <strncmp>
400399: 85 c0 test %eax,%eax
40039b: 75 0c jne 4003a9 <main+0xb9>
40039d: bf 4b 0b 40 00 mov $0x400b4b,%edi
4003a2: e8 1b ff ff ff callq 4002c2 <my_print>
There is a first my_print
function call, that is actually printing the message Checking password
, and after that, there is a call to strncmp
, a C function to compare two strings :o.
The combination of test
and jne
will make the program jump if the value returned by strncmp
is not zero (the return value is in the register EAX
). In this case, if we just remove the jne
instruction we should get our OK message.
Patching the Binary like a PRO
You can patch the binary file using many different tools, Iām going to use a nifty trick Iāve recently learned and that I think is pretty hackish.
So, open the binary file in vim
. Yes, thatās right. If vim
is not your preferred text editor, what follows may change your mind.
Then type the following:
ESC
+ :%!xxd
+ ENTER
Pressing the ESC
key instructs vim
to go in the so-called command mode. The colon :
will allow us to enter a command. The %
is a wildcard meaning that, whatever command follows, has to be applied to the whole text. Finally the !
will run a shell command, passing as input the current vim
buffer. The result of the shell command will be read back in vim
, substituting the old data.
So, try that, and you will get an hexdump of the binary that you can edit within vim
as a normal text file.
Now, back to our patching task, look for the instruction we where interested on: 0x75 0x0c
(check the objdump
output above).
For doing that type /75 0c
to get to the instruction. You should see something like this
...
0000390: ce48 89c7 e8c4 0500 0085 c075 0cbf 4b0b .H.........u..K.
00003a0: 4000 e81b ffff ffeb 0abf 5f0b 4000 e80f @........._.@...
...
In the hexdump area change 75
and 0c
to 90 90
(reverse conversion will not work if you change the ASCII dump on the right).
...
0000390: ce48 89c7 e8c4 0500 0085 c090 90bf 4b0b .H.........u..K.
00003a0: 4000 e81b ffff ffeb 0abf 5f0b 4000 e80f @........._.@...
...
The value 90
is the opcode for the nop
instruction. That means that we are just removing the conditional jump and letting the program go on and execute the success part of the comparison. We are effectively removing the check.
Now we have to write the binary data back to the file. First convert back the hexdump into binary data using ESC
+ :%!xxd -r
+ ENTER
. Then you just need to save and exit with the traditional :wq
.
Try to run the program now!
Cracking the Binary without Symbols
For this one (the one in the original post), we need to work a little bit moreā¦ but not much. Now, an objdump
, will not shown any information about functions or its nameā¦ Just one single assembly block. So, for this one we are going to use the string analysis method as suggested by @dtm.
He may had done it in a different way, but this is one way of doing it.
The idea is to locate the string containing the success message in the program, and then, figure out how the program ends up printing this string. In this case we are looking for the print and then we are moving back from there to find the condition that fires that print. So there we go!
First thing you have to know. String literals are read only data and, therefore, they get stored in the .rodata
section (ro
stands for read only). So, letās ask objdump
to show us what is in there.
$ objdump -s c1 | grep -A 15 .rodata
Contents of section .rodata:
40098c 30783030 20436861 6c6c656e 6765720a 0x00 Challenger.
40099c 0a005061 7373776f 72643a00 0a2b2043 ..Password:..+ C
4009ac 6865636b 696e6720 50617373 776f7264 hecking Password
4009bc 2000204f 4b2e0a2b 20596f75 20646964 . OK..+ You did
4009cc 20697421 0a002046 61696c2e 0a2d2054 it!.. Fail..- T
4009dc 72792061 6761696e 2e0a002b 20444f4e ry again...+ DON
4009ec 450a0062 cbb18628 7880b223 89f2c26d E..b...(x..#...m
4009fc 3ec7b223 89f2c26d 3ec7b223 89f2c26d >..#...m>..#...m
400a0c 3ec7b200 2389f2c2 6d3ec7b2 00002f64 >...#...m>..../d
400a1c 65762f75 72616e64 6f6d004c 445f5052 ev/urandom.LD_PR
400a2c 454c4f41 44007661 6c677269 6e6400 ELOAD.valgrind.
We can easily find the string " OK\n+ YOu did it!"
. This string is located at 0x4009bc + 2 = 0x4009be
. Now we just have to search for that address in the assembly code.
NOTE: If you do not want to add hex numbers and you have installed radare, try the command iz
$ objdump -d c1 | grep -C 5 4009be
400198: 48 8b 35 61 0e 20 00 mov 0x200e61(%rip),%rsi # 0x601000
40019f: 48 89 df mov %rbx,%rdi
4001a2: ba 20 00 00 00 mov $0x20,%edx
4001a7: e8 5a 06 00 00 callq 0x400806
4001ac: 85 c0 test %eax,%eax
4001ae: bf be 09 40 00 mov $0x4009be,%edi
4001b3: 74 05 je 0x4001ba
4001b5: bf d2 09 40 00 mov $0x4009d2,%edi
4001ba: e8 ed 00 00 00 callq 0x4002ac
4001bf: bf e7 09 40 00 mov $0x4009e7,%edi
4001c4: e8 e3 00 00 00 callq 0x4002ac
We can see how address 0x4001ae
loads the edi
register with the pointer to the string we are interested on (the success message or good boy). And just below, we find a jump if Equal (je
), otherwise, we load edi
with a pointer to the fail message (check the .rodata dump above if you do not believe meā¦ thatās the bad boy).
To crack/patch this one we can proceed in two different ways:
- Change the
je
conditional jump into a normal jumpā¦ changing the byte0x74
to0xeb
(this actually conversje
injmp
. NOP
themov
loadingedi
with the fail message.
In this case both will work, but in the general case, solution 1 is preferred. The reason is that the code to be executed is probably more complex that just loading a register with a string address and we may not be done with just writing 5 NOP
s.
Soā¦ go an patch the binary. Now you know how to do it with vim
!
Wrapping up
The title of this post said, āSolution and moreā. You may be wondering what was the more in the title. Well, by now :
- ā¦ you have already learned how to patch binaries with
vim
. That is pretty awesome, and also useful - ā¦ you have also learned how to convert a binary into a text file and recover itā¦ I have an curious idea on how to take advantage of this.
There is a third thing, that I will save for another post, but just in case you havenāt noticed itā¦ we had build a 5Kb STATIC binary. Just for you to realise what this means:
$ cat << EOP > a.c; gcc -static -o a a.c && ls -lh a.out
> int main(){return 0;}
> EOP
-rwxrwxr-x 1 pico pico 859K May 24 17:22 a.out
Yes. A static binary that does nothing takes more than 800Kb!!!
Happy Hacking!