Since another question was based on the same binary and the accepted answer doesn't go into detail on how to find the function responsible, here's a writeup.
Usual stuff $ r2 wysiNwyg; aaa
have a look at the list of functions(afl)
[0x080484a0]> afl
0x080483bc 3 35 fcn.080483bc
0x080483f0 1 6 sym.imp.strcmp
0x08048400 1 6 sym.imp.printf
0x08048410 1 6 sym.imp.fgets
0x08048420 1 6 sym.imp.puts
0x08048430 1 6 loc.imp.__gmon_start
0x08048440 1 6 sym.imp.exit
0x08048450 1 6 sym.imp.strlen
0x08048460 1 6 sym.imp.__libc_start_main
0x08048470 1 6 sym.imp.memset
0x08048480 1 6 sym.imp.putchar
0x08048490 1 6 sym.imp.ptrace
0x080484a0 1 33 entry0
0x080484d0 1 4 fcn.080484d0
0x080484e0 4 43 fcn.080484e0
0x08048550 3 30 entry3.fini
0x08048570 8 43 -> 93 entry1.init
0x0804859b 3 55 entry2.init
0x080485d2 12 446 entry4.fini
0x08048790 8 250 main
Other than main
, entry4.fini
function is quite large. Have a look at .fini
and .fini_array
sections from the binary(iS). Functions from .fini_array
are called when the program is about to terminate(after main).
[0x080484a0]> iS~fini
14 0x00000904 20 0x08048904 20 -r-x .fini
19 0x00000c08 8 0x08049c08 8 -rw- .fini_array
Seek to that address(s). Dump .fini_array
(pxw).
[0x080484a0]> s 0x08049c08
[0x08049c08]> pxw 0x10
0x08049c08 0x08048550 0x080485d2 0x00000000 0x00000001 P...............
entry4.fini
has been referenced in .fini_array
. Go to main and disassemble. Check where the input is getting stored to
│ 0x080487d5 a1409d0408 mov eax, dword [obj.stdin] ; [0x8049d40:4]=0
│ 0x080487da 83ec04 sub esp, 4
│ 0x080487dd 50 push eax ; FILE *stream
│ 0x080487de 6a23 push 0x23 ; '#' ; 35 ; int size
│ 0x080487e0 68609d0408 push 0x8049d60 ; char *s
│ 0x080487e5 e826fcffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
Input from fgets is going to 0x8049d60. You can also name it f input 35 @ 0x8049d60
. Check for xrefs on it
[0x08048790]> axt 0x8049d60
entry4.fini 0x80486ec [DATA] push 0x8049d60
main 0x80487c8 [DATA] push 0x8049d60
main 0x80487e0 [DATA] push 0x8049d60
main 0x80487f8 [DATA] push 0x8049d60
main 0x8048808 [DATA] movzx eax, byte [eax + 0x8049d60]
main 0x8048816 [DATA] push 0x8049d60
main 0x8048826 [DATA] mov byte [eax + 0x8049d60], 0
main 0x8048835 [DATA] push 0x8049d60
main 0x804885b [DATA] push 0x8049d60
entry4.fini
also references the input. Disassemble it.
│ 0x080486ec 68609d0408 push 0x8049d60 ; const char *s
│ 0x080486f1 e85afdffff call sym.imp.strlen ; size_t strlen(const char *s)
│ 0x080486f6 83c410 add esp, 0x10
│ 0x080486f9 83f822 cmp eax, 0x22 ; '"' ; 34
First check is if your input is 34 bytes long.
│ 0x080486f9 83f822 cmp eax, 0x22 ; '"' ; 34
│ ┌─< 0x080486fc 7405 je 0x8048703
│ ┌──< 0x080486fe e988000000 jmp 0x804878b
│ ││ ; CODE XREF from entry4.fini (0x80486fc)
│ │└─> 0x08048703 c745f4000000. mov dword [local_ch], 0
│ │┌─< 0x0804870a eb2c jmp 0x8048738
│ ││ ; CODE XREF from entry4.fini (0x804873c)
│ ┌───> 0x0804870c 8d55d1 lea edx, [local_2fh]
│ ⁝││ 0x0804870f 8b45f4 mov eax, dword [local_ch]
│ ⁝││ 0x08048712 01d0 add eax, edx
│ ⁝││ 0x08048714 0fb600 movzx eax, byte [eax]
│ ⁝││ 0x08048717 0fbed0 movsx edx, al
│ ⁝││ 0x0804871a 8b45f4 mov eax, dword [local_ch]
│ ⁝││ 0x0804871d 05609d0408 add eax, 0x8049d60
│ ⁝││ 0x08048722 0fb600 movzx eax, byte [eax]
│ ⁝││ 0x08048725 83f033 xor eax, 0x33
│ ⁝││ 0x08048728 0fbec0 movsx eax, al
│ ⁝││ 0x0804872b 0fb6c0 movzx eax, al
│ ⁝││ 0x0804872e 39c2 cmp edx, eax
│ ┌────< 0x08048730 7402 je 0x8048734
│ ┌─────< 0x08048732 eb57 jmp 0x804878b
│ ││⁝││ ; CODE XREF from entry4.fini (0x8048730)
│ │└────> 0x08048734 8345f401 add dword [local_ch], 1
│ │ ⁝││ ; CODE XREF from entry4.fini (0x804870a)
│ │ ⁝│└─> 0x08048738 837df421 cmp dword [local_ch], 0x21 ; [0x21:4]=-1 ; '!' ; 33
│ │ └───< 0x0804873c 7ece jle 0x804870c
│ │ │ 0x0804873e c745f4000000. mov dword [local_ch], 0
│ │ │┌─< 0x08048745 eb21 jmp 0x8048768
Second check involves xor'ing each byte from local_2fh
with 0x33 and the comparing with the input byte by byte. Start the VM to dump the memory(0x22 bytes from local_2fh) and perform the xor operation.
[0x080484a0]> s entry4.fini
[0x080485d2]> aei
[0x080485d2]> aeim
[0x080485d2]> aeip
Local variables are assigned up to instruction 0x08048665 for the check. Emulate and dump the values.
[0x080485d2]> aesu 0x08048665
[0x08048659]> pcp 0x22 @ebp-0x2f
import struct
buf = struct.pack ("34B", *[
0x02,0x5d,0x02,0x67,0x6c,0x07,0x5d,0x77,0x6c,0x75,0x02,
0x5d,0x02,0x6c,0x07,0x41,0x61,0x07,0x6a,0x40,0x6c,0x07,
0x41,0x00,0x6c,0x60,0x03,0x6c,0x00,0x07,0x40,0x6a,0x12,
0x12])
[0x00000041]> !python
Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import struct
>>> buf = struct.pack ("34B", *[
... 0x02,0x5d,0x02,0x67,0x6c,0x07,0x5d,0x77,0x6c,0x75,0x02,
... 0x5d,0x02,0x6c,0x07,0x41,0x61,0x07,0x6a,0x40,0x6c,0x07,
... 0x41,0x00,0x6c,0x60,0x03,0x6c,0x00,0x07,0x40,0x6a,0x12,
... 0x12])
>>> print ''.join(map(lambda x:chr(ord(x)^0x33),buf))
1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!
This works
./wysiNwyg
#########################################################
### Welcome to the "wysiNwyg" challenge!
### Your task is to find out the password to be able
### to decrypt the password-protected zip and read
### the secret flag. Good Luck!!
#########################################################
Password: 1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!
Congratulations! You just won :p
iS
,aa
andafl
are your friends. Additionally trace an execution using pintool or r2's debugger to see what "hidden" code is executing.