Another crackme from crp- : bf (local mirror). This time, no GDB, only radare2 !
[0x080486d0]> aa
[0x080486d0]> pdf
0x080486d0 section..text:
0x080486d0 31ed xor ebp, ebp ; [12] va=0x080486d0 pa=0x000006d0 sz=1952 vsz=1952 rwx=-r-x .text
0x080486d2 5e pop esi
0x080486d3 89e1 mov ecx, esp
0x080486d5 83e4f0 and esp, 0xfffffff0
0x080486d8 50 push eax
0x080486d9 54 push esp
0x080486da 52 push edx
0x080486db 68f08d0408 push dword 0x8048df0
0x080486e0 68908d0408 push dword 0x8048d90
0x080486e5 51 push ecx
0x080486e6 56 push esi
0x080486e7 68ac890408 push dword 0x80489ac
0x080486ec e837ffffff call dword imp.__libc_start_main
main() seems to be at 0x80489ac
pdf@0x80489ac
/ function: main (166)
| 0x080489ac main:
| 0x080489ac 55 push ebp
| 0x080489ad 89e5 mov ebp, esp
| 0x080489af 56 push esi
| 0x080489b0 53 push ebx
| 0x080489b1 83e4f0 and esp, 0xfffffff0
| 0x080489b4 83ec10 sub esp, 0x10
| 0x080489b7 e8e8fdffff call dword fcn.080487a4
| 0x080489bc 89c6 mov esi, eax
| 0x080489be e820feffff call dword fcn.080487e3
| 0x080489c3 89c3 mov ebx, eax
| 0x080489c5 83ec0c sub esp, 0xc
| 0x080489c8 68c48e0408 push dword str.bf.crp
| 0x080489cd e816fcffff call dword imp.puts
| 0x080489d2 83c410 add esp, 0x10
| 0x080489d5 85db test ebx, ebx
| ,=< 0x080489d7 7512 jnz loc.080489eb
| | 0x080489d9 83ec0c sub esp, 0xc
| | 0x080489dc 68ab8e0408 push dword str.nokey.
| | 0x080489e1 e802fcffff call dword imp.puts
| | 0x080489e6 83c410 add esp, 0x10
| ,==< 0x080489e9 eb40 jmp loc.08048a2b
| || ; CODE (JMP) XREF 0x080489d7 (main)
/ loc: loc.080489eb (103)
| || 0x080489eb loc.080489eb:
| |`-> 0x080489eb 83ec0c sub esp, 0xc
| | 0x080489ee 68b38e0408 push dword str.checkingkey
| | 0x080489f3 e8f0fbffff call dword imp.puts
| | ; imp.puts()
| | 0x080489f8 83c408 add esp, 0x8
| | 0x080489fb 53 push ebx
| | 0x080489fc 56 push esi
| | 0x080489fd e8e1feffff call dword fcn.080488e3
| | ; fcn.080488e3(unk, unk)
| | 0x08048a02 83c410 add esp, 0x10
| | 0x08048a05 85c0 test eax, eax
| ,===< 0x08048a07 7412 jz loc.08048a1b
| || 0x08048a09 83ec0c sub esp, 0xc
| || 0x08048a0c 68f88e0408 push dword str.ACCEPTED
| || 0x08048a11 e8d2fbffff call dword imp.puts
| || ; imp.puts()
| || 0x08048a16 83c410 add esp, 0x10
| ,====< 0x08048a19 eb10 jmp loc.08048a2b
| || ; CODE (JMP) XREF 0x08048a07 (main)
/ loc: loc.08048a1b (55)
| || 0x08048a1b loc.08048a1b:
| |`---> 0x08048a1b 83ec0c sub esp, 0xc
| | | 0x08048a1e 682c8f0408 push dword str.INVALID
| | | 0x08048a23 e8c0fbffff call dword imp.puts
| | | ; imp.puts()
| | | 0x08048a28 83c410 add esp, 0x10
| | | ; CODE (JMP) XREF 0x08048a19 (main)
| | | ; CODE (JMP) XREF 0x080489e9 (main)
/ loc: loc.08048a2b (39)
| | | 0x08048a2b loc.08048a2b:
| `-`--> 0x08048a2b 85f6 test esi, esi
| ,=====< 0x08048a2d 740c jz loc.08048a3b
| | 0x08048a2f 83ec0c sub esp, 0xc
| | 0x08048a32 56 push esi
| | 0x08048a33 e840fcffff call dword imp.free
| | ; imp.free()
| | 0x08048a38 83c410 add esp, 0x10
| | ; CODE (JMP) XREF 0x08048a2d (main)
/ loc: loc.08048a3b (23)
| | 0x08048a3b loc.08048a3b:
| `-----> 0x08048a3b 85db test ebx, ebx
| ,======< 0x08048a3d 740c jz loc.08048a4b
| | 0x08048a3f 83ec0c sub esp, 0xc
| | 0x08048a42 53 push ebx
| | 0x08048a43 e830fcffff call dword imp.free
| | ; imp.free()
| | 0x08048a48 83c410 add esp, 0x10
| | ; CODE (JMP) XREF 0x08048a3d (main)
/ loc: loc.08048a4b (7)
| | 0x08048a4b loc.08048a4b:
| `------> 0x08048a4b 8d65f8 lea esp, [ebp-0x8]
| 0x08048a4e 5b pop ebx
| 0x08048a4f 5e pop esi
| 0x08048a50 5d pop ebp
\ 0x08048a51 c3 ret
Woa, shitloads of stuffs here. First, two functions are called: fcn.080487a4 and fcn.080487e3.
/ function: fcn.080487a4 (63)
| 0x080487a4 fcn.080487a4:
| 0x080487a4 55 push ebp
| 0x080487a5 89e5 mov ebp, esp
| 0x080487a7 83ec18 sub esp, 0x18
| 0x080487aa e889feffff call dword imp.getuid
| 0x080487af 890424 mov [esp], eax
| 0x080487b2 e801feffff call dword imp.getpwuid
| 0x080487b7 83c410 add esp, 0x10
| 0x080487ba 85c0 test eax, eax
| ,=< 0x080487bc 7519 jnz loc.080487d7
| | 0x080487be 83ec0c sub esp, 0xc
| | 0x080487c1 68948e0408 push dword str.getpwuid
| | 0x080487c6 e8cdfdffff call dword imp.perror
| | 0x080487cb c7042400000000 mov dword [esp], 0x0
| | 0x080487d2 e891feffff call dword imp.exit
| `-> 0x080487d7 83ec0c sub esp, 0xc
| 0x080487da ff30 push dword [eax]
| 0x080487dc e877feffff call dword imp.__strdup
| 0x080487e1 c9 leave
\ 0x080487e2 c3 ret
A call to getuid(), and another one to getpwuid(). If you open the manpage, you'll see that thoses functions involve some struct magic. Fear not, this function is passing something to strdup, that takes a char* as parameter. Since there is no bit-twidling magic here, this function likely returns our username (the first member of the passwd structure).
/ function: fcn.080487e3 (136)
| 0x080487e3 55 push ebp
| 0x080487e4 89e5 mov ebp, esp
| 0x080487e6 57 push edi
| 0x080487e7 56 push esi
| 0x080487e8 53 push ebx
| 0x080487e9 83ec14 sub esp, 0x14
| 0x080487ec 689f8e0408 push dword 0x8048e9f
| 0x080487f1 68a18e0408 push dword str.key.bf
| 0x080487f6 e89dfeffff call dword imp.fopen
| 0x080487fb 89c6 mov esi, eax
| 0x080487fd 83c410 add esp, 0x10
| 0x08048800 b800000000 mov eax, 0x0
| 0x08048805 85f6 test esi, esi
| ,=< 0x08048807 745a jz loc.08048863
| | 0x08048809 83ec04 sub esp, 0x4
| | 0x0804880c 6a02 push 0x2
| | 0x0804880e 6a00 push 0x0
| | 0x08048810 56 push esi
| | 0x08048811 e802feffff call dword imp.fseek
| | 0x08048816 893424 mov [esp], esi
| | 0x08048819 e8bafdffff call dword imp.ftell
| | 0x0804881e 89c7 mov edi, eax
| | 0x08048820 893424 mov [esp], esi
| | 0x08048823 e8a0fdffff call dword imp.rewind
| | 0x08048828 83c410 add esp, 0x10
| | 0x0804882b 81ff00100000 cmp edi, 0x1000
| ,==< 0x08048831 7e10 jle loc.08048843
| || 0x08048833 83ec0c sub esp, 0xc
| || 0x08048836 56 push esi
| || 0x08048837 e80cfeffff call dword imp.fclose
| || 0x0804883c b800000000 mov eax, 0x0
| ,===< 0x08048841 eb20 jmp loc.08048863
| |`--> 0x08048843 83ec0c sub esp, 0xc
| | | 0x08048846 57 push edi
| | | 0x08048847 e8acfdffff call dword imp.malloc
| | | 0x0804884c 89c3 mov ebx, eax
| | | 0x0804884e 56 push esi
| | | 0x0804884f 6a01 push 0x1
| | | 0x08048851 57 push edi
| | | 0x08048852 50 push eax
| | | 0x08048853 e8b0fdffff call dword imp.fread
| | | 0x08048858 83c414 add esp, 0x14
| | | 0x0804885b 56 push esi
| | | 0x0804885c e8e7fdffff call dword imp.fclose
| | | 0x08048861 89d8 mov eax, ebx
| `-`-> 0x08048863 8d65f4 lea esp, [ebp-0xc]
| 0x08048866 5b pop ebx
| 0x08048867 5e pop esi
| 0x08048868 5f pop edi
| 0x08048869 5d pop ebp
\ 0x0804886a c3 ret
Arg, another megaton of asm.
If you're like me: clever lazy, you saw the call to fopen, with "key.bf" as parameter.
lseek, fclose, malloc, fread, fclose, blablabla. I'm pretty sure this function is reading key.bf's content.
After those two function, there is a call to fcn.080488e3, and then the classic ACCEPTED/INVALID choice.
/ function: fcn.080488e3 (201)
| 0x080488e3 fcn.080488e3:
| 0x080488e3 55 push ebp
| 0x080488e4 89e5 mov ebp, esp
| 0x080488e6 57 push edi
| 0x080488e7 56 push esi
| 0x080488e8 53 push ebx
| 0x080488e9 81ec50500000 sub esp, 0x5050
| 0x080488ef 8b7d08 mov edi, [ebp+0x8]
| 0x080488f2 be00000000 mov esi, 0x0
| 0x080488f7 57 push edi
| 0x080488f8 ff750c push dword [ebp+0xc]
| 0x080488fb 8d85c8bfffff lea eax, [ebp+0xffffbfc8]
| 0x08048901 50 push eax
| 0x08048902 e84d010000 call dword fcn.08048a54
| 0x08048907 83c410 add esp, 0x10
| ,=< 0x0804890a eb2b jmp loc.08048937
| .---> 0x0804890c 46 inc esi
| | | 0x0804890d 83ec0c sub esp, 0xc
| | | 0x08048910 6a2e push 0x2e
| | | 0x08048912 e891fcffff call dword imp.putchar
| | | 0x08048917 ba32000000 mov edx, 0x32
| | | 0x0804891c 89f0 mov eax, esi
| | | 0x0804891e 89d1 mov ecx, edx
| | | 0x08048920 99 cdq
| | | 0x08048921 f7f9 idiv ecx
| | | 0x08048923 83c410 add esp, 0x10
| | | 0x08048926 85d2 test edx, edx
| |,==< 0x08048928 750d jnz loc.08048937
| ||| 0x0804892a 83ec0c sub esp, 0xc
| ||| 0x0804892d 6a0a push 0xa
| ||| 0x0804892f e874fcffff call dword imp.putchar
| ||| 0x08048934 83c410 add esp, 0x10
| |``-> 0x08048937 83ec0c sub esp, 0xc
| | 0x0804893a 8d85c8bfffff lea eax, [ebp+0xffffbfc8]
| | 0x08048940 50 push eax
| | 0x08048941 e898010000 call dword fcn.08048ade
| | 0x08048946 89c3 mov ebx, eax
| | 0x08048948 83c410 add esp, 0x10
| | 0x0804894b 85c0 test eax, eax
| `===< 0x0804894d 74bd jz loc.0804890c
| 0x0804894f 83ec0c sub esp, 0xc
| 0x08048952 6a0a push 0xa
| 0x08048954 e84ffcffff call dword imp.putchar
| 0x08048959 83c408 add esp, 0x8
| 0x0804895c 8db5b8bfffff lea esi, [ebp+0xffffbfb8]
| 0x08048962 56 push esi
| 0x08048963 57 push edi
| 0x08048964 e802ffffff call dword fcn.0804886b
| 0x08048969 83c410 add esp, 0x10
| 0x0804896c 83fb02 cmp ebx, 0x2
| ,====< 0x0804896f 742c jz loc.0804899d
| | 0x08048971 83ec08 sub esp, 0x8
| | 0x08048974 8d9db8afffff lea ebx, [ebp+0xffffafb8]
| | 0x0804897a 53 push ebx
| | 0x0804897b 8d85c8bfffff lea eax, [ebp+0xffffbfc8]
| | 0x08048981 50 push eax
| | 0x08048982 e82a020000 call dword fcn.08048bb1
| | 0x08048987 83c408 add esp, 0x8
| | 0x0804898a 56 push esi
| | 0x0804898b 53 push ebx
| | 0x0804898c e8f7fbffff call dword imp.strcmp
| | 0x08048991 83c410 add esp, 0x10
| | 0x08048994 ba01000000 mov edx, 0x1
| | 0x08048999 85c0 test eax, eax
| ,=====< 0x0804899b 7405 jz loc.080489a2
| |`----> 0x0804899d ba00000000 mov edx, 0x0
| `-----> 0x080489a2 89d0 mov eax, edx
| 0x080489a4 8d65f4 lea esp, [ebp-0xc]
| 0x080489a7 5b pop ebx
| 0x080489a8 5e pop esi
| 0x080489a9 5f pop edi
| 0x080489aa 5d pop ebp
\ 0x080489ab c3 ret
And another behemoth. Ho, a strcmp ! But since this is a crackme from crp-, this won't be so simple. The key is likely transformed before the strcmp.
If you look carefully at the arrows, you can notice that only one arrow is going upward. This is likely to be the key verification loop. The call to putchar() in this loops gets 0xa as parameter, also known as "." (man ascii if you don;t believe me) ! If you put a random key, you can see something like this:
--[ bf.crp- ]-------------------------------------
checking key:
..................................................
.....
---------------------------------------[INVALID]--
The number of dots corresponds to the lenght of bf.key's content. The only function called in this loop is fcn.08048ade.
/ function: fcn.08048ade (211)
| 0x08048ade fcn.08048ade:
| 0x08048ade 55 push ebp
| 0x08048adf 89e5 mov ebp, esp
| 0x08048ae1 53 push ebx
| 0x08048ae2 83ec04 sub esp, 0x4
| 0x08048ae5 8b5d08 mov ebx, [ebp+0x8]
| 0x08048ae8 833b00 cmp dword [ebx], 0x0
| ,=< 0x08048aeb 7407 jz loc.08048af4
| | 0x08048aed 8b03 mov eax, [ebx]
| ,==< 0x08048aef e9b8000000 jmp dword loc.08048bac
| |`-> 0x08048af4 817b04ff0f0000 cmp dword [ebx+0x4], 0xfff
| ,===< 0x08048afb 7f0a jg loc.08048b07
| || 0x08048afd 8b4304 mov eax, [ebx+0x4]
| || 0x08048b00 807c030800 cmp byte [ebx+eax+0x8], 0x0
| ,====< 0x08048b05 7510 jnz loc.08048b17
| |`---> 0x08048b07 c70301000000 mov dword [ebx], 0x1
| | | 0x08048b0d b801000000 mov eax, 0x1
| ,=====< 0x08048b12 e995000000 jmp dword loc.08048bac
| |`----> 0x08048b17 8b4304 mov eax, [ebx+0x4]
| | | 0x08048b1a 0fbe440308 movsx eax, byte [ebx+eax+0x8]
| | | 0x08048b1f 83e82b sub eax, 0x2b
| | | 0x08048b22 83f832 cmp eax, 0x32
| ,======< 0x08048b25 7777 ja loc.08048b9e
| || | 0x08048b27 ff2485608f0408 jmp dword [eax*4+0x8048f60]
| || | 0x08048b2e 83ec0c sub esp, 0xc
| || | 0x08048b31 53 push ebx
| || | 0x08048b32 e893000000 call dword fcn.08048bca
| || | 0x08048b37 83c410 add esp, 0x10
| ,=======< 0x08048b3a eb6e jmp loc.08048baa
| ||| | 0x08048b3c 83ec0c sub esp, 0xc
| ||| | 0x08048b3f 53 push ebx
| ||| | 0x08048b40 e8b2000000 call dword fcn.08048bf7
| ||| | 0x08048b45 83c410 add esp, 0x10
| ========< 0x08048b48 eb60 jmp loc.08048baa
| ||| | 0x08048b4a 83ec0c sub esp, 0xc
| ||| | 0x08048b4d 53 push ebx
| ||| | 0x08048b4e e8ce000000 call dword fcn.08048c21
| ||| | 0x08048b53 83c410 add esp, 0x10
| ========< 0x08048b56 eb52 jmp loc.08048baa
| ||| | 0x08048b58 83ec0c sub esp, 0xc
| ||| | 0x08048b5b 53 push ebx
| ||| | 0x08048b5c e8d8000000 call dword fcn.08048c39
| ||| | 0x08048b61 83c410 add esp, 0x10
| ========< 0x08048b64 eb44 jmp loc.08048baa
| ||| | 0x08048b66 83ec0c sub esp, 0xc
| ||| | 0x08048b69 53 push ebx
| ||| | 0x08048b6a e8e2000000 call dword fcn.08048c51
| ||| | 0x08048b6f 83c410 add esp, 0x10
| ========< 0x08048b72 eb36 jmp loc.08048baa
| ||| | 0x08048b74 83ec0c sub esp, 0xc
| ||| | 0x08048b77 53 push ebx
| ||| | 0x08048b78 e814010000 call dword fcn.08048c91
| ||| | 0x08048b7d 83c410 add esp, 0x10
| ========< 0x08048b80 eb28 jmp loc.08048baa
| ||| | 0x08048b82 83ec0c sub esp, 0xc
| ||| | 0x08048b85 53 push ebx
| ||| | 0x08048b86 e85e010000 call dword fcn.08048ce9
| ||| | 0x08048b8b 83c410 add esp, 0x10
| ========< 0x08048b8e eb1a jmp loc.08048baa
| ||| | 0x08048b90 83ec0c sub esp, 0xc
| ||| | 0x08048b93 53 push ebx
| ||| | 0x08048b94 e8a9010000 call dword fcn.08048d42
| ||| | 0x08048b99 83c410 add esp, 0x10
| ========< 0x08048b9c eb0c jmp loc.08048baa
| |`------> 0x08048b9e 83ec0c sub esp, 0xc
| | | | 0x08048ba1 53 push ebx
| | | | 0x08048ba2 e8d5010000 call dword fcn.08048d7c
| | | | 0x08048ba7 83c410 add esp, 0x10
| `-------> 0x08048baa 8b03 mov eax, [ebx]
| `--`--> 0x08048bac 8b5dfc mov ebx, [ebp-0x4]
| 0x08048baf c9 leave
\ 0x08048bb0 c3 ret
Ok, this will hopefully be the last HUGE pice of code of this blogpost. Did you notice something weird in this listing ? No ? Chek again. Here is the answer:
jmp dword [eax*4+0x8048f60]
This awfully looks like a switch-case jump ! Radare is unable to tell us what are the cases values (but I sent a bugreport :) ), but with a bit of luck, it's the ASCII code:
62is>60is<43is+45is-46is.44is,91is[93is]
Mh. This remind me something. A weird language. I even wrote a compiler for it. It's Brainfuck of course !
A simple check is to compare the third and the fourth function. The sole difference between them should be a "inc" instead of a "dec".
/ function: fcn.08048c21 (24)
| 0x08048c21 fcn.08048c21:
| 0x08048c21 55 push ebp
| 0x08048c22 89e5 mov ebp, esp
| 0x08048c24 8b4508 mov eax, [ebp+0x8]
| 0x08048c27 8b9008100000 mov edx, [eax+0x1008]
| 0x08048c2d fe84020c100000 inc byte [edx+eax+0x100c]
| 0x08048c34 ff4004 inc dword [eax+0x4]
| 0x08048c37 5d pop ebp
\ 0x08048c38 c3 ret
[0x080486d0]> pdf@0x8048C39
/ function: fcn.08048c39 (24)
| 0x08048c39 fcn.08048c39:
| 0x08048c39 55 push ebp
| 0x08048c3a 89e5 mov ebp, esp
| 0x08048c3c 8b4508 mov eax, [ebp+0x8]
| 0x08048c3f 8b9008100000 mov edx, [eax+0x1008]
| 0x08048c45 fe8c020c100000 dec byte [edx+eax+0x100c]
| 0x08048c4c ff4004 inc dword [eax+0x4]
| 0x08048c4f 5d pop ebp
\ 0x08048c50 c3 ret
Yeah, we were right !
But what are we computing is brainfuck ? Before the strcmp function, there is a unique call to a function: fcn.0804886b
/ function: fcn.0804886b (120)
| 0x0804886b fcn.0804886b:
| 0x0804886b 55 push ebp
| 0x0804886c 89e5 mov ebp, esp
| 0x0804886e 57 push edi
| 0x0804886f 56 push esi
| 0x08048870 53 push ebx
| 0x08048871 83ec0c sub esp, 0xc
| 0x08048874 8b7508 mov esi, [ebp+0x8]
| 0x08048877 bb00000000 mov ebx, 0x0 ; ebx is use as accumulation variable
| 0x0804887c 89f7 mov edi, esi
| 0x0804887e fc cld
| 0x0804887f b9ffffffff mov ecx, 0xffffffff
| 0x08048884 b000 mov al, 0x0
| 0x08048886 f2ae repne scasb
| 0x08048888 f7d1 not ecx
| 0x0804888a 49 dec ecx
| 0x0804888b 83f928 cmp ecx, 0x28 ; strlen(key) > 0x28 ?
| ,=< 0x0804888e 7e05 jle loc.08048895 ; not
| | 0x08048890 b928000000 mov ecx, 0x28 ; strlen(key) = 0x28
| `-> 0x08048895 ba00000000 mov edx, 0x0 ; edx is used as a counter
| 0x0804889a 39ca cmp edx, ecx
| ,==< 0x0804889c 7d0d jge loc.080488ab ; jumps to the end of the loop
| .---> 0x0804889e 0fbe0416 movsx eax, byte [esi+edx]
| || 0x080488a2 01c3 add ebx, eax ; ebx += key[edx]
| || 0x080488a4 01cb add ebx, ecx ; ebx += strlent(key)
| || 0x080488a6 42 inc edx ; increments the counter
| || 0x080488a7 39ca cmp edx, ecx ; are we at the end of the loop ?
| `===< 0x080488a9 7cf3 jl loc.0804889e
| `--> 0x080488ab 0fafd9 imul ebx, ecx ; ebx *= strlen(key)
| 0x080488ae c1e30a shl ebx, 0xa ; ebx = ebx << 0xa
| 0x080488b1 ba00000000 mov edx, 0x0 ; edx is used as a counter once again
| 0x080488b6 39ca cmp edx, ecx
| ,====< 0x080488b8 7d0d jge loc.080488c7
| .-----> 0x080488ba 0fbe0416 movsx eax, byte [esi+edx]
| || 0x080488be 29c3 sub ebx, eax ; ebx = ebx - key[ebx]
| || 0x080488c0 01d3 add ebx, edx ; ebx = ebx + ebx
| || 0x080488c2 42 inc edx ; edx ++
| || 0x080488c3 39ca cmp edx, ecx ; are we at the end of the loop ?
| `=====< 0x080488c5 7cf3 jl loc.080488ba
| `----> 0x080488c7 8d1c5b lea ebx, [ebx+ebx*2] ; ebx = ebx + ebx * 2 = 3 * ebx
| 0x080488ca 83ec04 sub esp, 0x4
| 0x080488cd 53 push ebx
| 0x080488ce 68a88e0408 push dword 0x8048ea8
| 0x080488d3 ff750c push dword [ebp+0xc]
| 0x080488d6 e8cdfdffff call dword imp.sprintf
| 0x080488db 8d65f4 lea esp, [ebp-0xc]
| 0x080488de 5b pop ebx
| 0x080488df 5e pop esi
| 0x080488e0 5f pop edi
| 0x080488e1 5d pop ebp
\ 0x080488e2 c3 ret
Okay, this time, we must reverse it, no more guessing.
repne scasb
This instruction is often used in strlen().
Here is a rought translation of this function in Python:
def function(key):
ret = 0
l = len(key)
if l > 0x28:
l = 0x28
for i in range(key):
ret = ret + l + i
ret = ret + l
ret = ret << 0xa
for i,j in enumerate(key):
ret = i + ret + j
return 3 * ret
Our key is likely a brainfuck program that will generate this number. Brainfuck has native input/output support, and this program takes our key as input, and output goodboy/badboy.
With a little bit of luck:
-
The "," operator (read) pops a letter of our username, and put it in the register pointed by the pointer.
-
The "." operator (write) put the value pointed by the pointer to the stuff tha is going to be compared with the previously computed key.
This seems to be (a little bit) confirmed by the README, which give bonus points for a generic keyfile.
Keygen
import getpass
def compute_key(name):
key = 0
l = len(name)
if l > 0x28:
l = 0x28
for i in name:
key = key + l + ord(i)
key *= l
key <<= 0xa
for i,j in enumerate(name):
key += i - ord(j)
return str(3 * key)
def generate_brainfuck():
out = ''
for i,j in zip(username, key):
out += ',' + '-' * (ord(i) - ord(j)) + '.'
for i in key[len(username):]:
out += ',' + '+' * ord(i) + '.'
return out
print ".: crp-'s bf keygen - jvoisin :."
username = getpass.getuser()
print "[x] Username: %s" % username
key = compute_key(username)
print "[x] Key: %s" % key
out = generate_brainfuck()
print "[x] Brainfuck generated"
with open('key.bf', 'w') as f:
f.write(out)
print "[x] Key written"
Generic keyfile
I generated a generic keyfile with brainfix, but even heavily tuned to spare place, the outputed code is still too big :/ I am too lazy to write a keygen in Brainfuck by hand.
This was a fun (but non-surprising) crackme. Thank you crp- !