=============== level7 tutorial =============== gdb-peda$ disas main Dump of assembler code for function main: 0x080488d0 <+0>: push %ebp 0x080488d1 <+1>: mov %esp,%ebp 0x080488d3 <+3>: sub $0x8,%esp 0x080488d6 <+6>: lea 0x8048a14,%eax 0x080488dc <+12>: mov %eax,(%esp) 0x080488df <+15>: call 0x8048410 0x080488e4 <+20>: mov %eax,-0x4(%ebp) 0x080488e7 <+23>: call 0x80486c0 0x080488ec <+28>: xor %eax,%eax 0x080488ee <+30>: add $0x8,%esp 0x080488f1 <+33>: pop %ebp 0x080488f2 <+34>: ret End of assembler dump. main() is simple. gdb-peda$ x/s 0x8048a14 0x8048a14: "Let's start an esoteric game!\n" main() { printf("Let's start an esoteric game!\n"); password(); } Let's move on to password(); It's long, but now we know how to deal with this. gdb-peda$ disas password Dump of assembler code for function password: 0x080486c0 <+0>: push %ebp 0x080486c1 <+1>: mov %esp,%ebp 0x080486c3 <+3>: push %ebx 0x080486c4 <+4>: push %edi 0x080486c5 <+5>: push %esi 0x080486c6 <+6>: sub $0x25c,%esp 0x080486cc <+12>: lea 0x80489a7,%eax gdb-peda$ x/s 0x80489a7 0x80489a7: "%50s" eax = "%50s" 0x080486d2 <+18>: lea -0x20c(%ebp),%ecx <-- 1st is from here. ebp_20c goes to the 1st argument of memset, then it would be a buffer. char buffer[0x200]; // buffer is ebp_20c; ecx = buffer; 0x080486d8 <+24>: xor %edx,%edx edx = 0; 0x080486da <+26>: mov $0x200,%esi 0x080486df <+31>: mov %ecx,(%esp) <-- 1st 0x080486e2 <+34>: movl $0x0,0x4(%esp) <-- 2nd 0x080486ea <+42>: movl $0x200,0x8(%esp) <-- 3rd 0x080486f2 <+50>: mov %eax,-0x218(%ebp) char *ebp_218 = "%50s"; 0x080486f8 <+56>: mov %ecx,-0x21c(%ebp) char *ebp_21c = buffer; // see +18 for %ecx, ecx is buffer. 0x080486fe <+62>: mov %edx,-0x220(%ebp) int ebp_220 = 0; // edx was zero by +24 0x08048704 <+68>: mov %esi,-0x224(%ebp) int ebp_224 = 0x200; // see +26, esi = 0x200. 0x0804870a <+74>: call 0x8048450 memset(buffer, 0, 0x200); 0x0804870f <+79>: mov -0x218(%ebp),%eax 0x08048715 <+85>: mov %eax,(%esp) <-- 1st argument, "%50s" 0x08048718 <+88>: mov -0x21c(%ebp),%ecx <-- 2nd comes from here. 0x0804871e <+94>: mov %ecx,0x4(%esp) <-- 2nd argument, ebp_21c, buffer! 0x08048722 <+98>: call 0x80484a0 <__isoc99_scanf@plt> scanf("%50s", buffer); 0x08048727 <+103>: lea 0x80489ac,%ecx <-- 1st is from here 0x0804872d <+109>: lea -0x20c(%ebp),%edx <-- 2nd is from here, buffer! 0x08048733 <+115>: mov %ecx,(%esp) <-- 1st arg 0x08048736 <+118>: mov %edx,0x4(%esp) <-- 2nd arg 0x0804873a <+122>: mov %eax,-0x228(%ebp) <-- return value of scanf, unused, not optimzed... 0x08048740 <+128>: call 0x8048410 gdb-peda$ x/s 0x80489ac 0x80489ac: "Your buffer: %s\n" printf("Your buffer: %s\n", buffer); 0x08048745 <+133>: lea -0x20c(%ebp),%ecx <-- 1st is from here, this is buffer 0x0804874b <+139>: mov %esp,%edx <-- now edx is esp 0x0804874d <+141>: mov %ecx,(%edx) <-- 1st arg 0x0804874f <+143>: mov %eax,-0x22c(%ebp) <-- return value of printf, unused, not optimized 0x08048755 <+149>: mov %ecx,-0x230(%ebp) char *ebp_230 = buffer; 0x0804875b <+155>: call 0x8048430 int length = strlen(buffer); 0x08048760 <+160>: mov %eax,-0x210(%ebp) int length == ebp_210. 0x08048766 <+166>: mov -0x210(%ebp),%eax eax = length; 0x0804876c <+172>: mov -0x230(%ebp),%ecx <-- 1st is from here, ebp_230 == buffer, so ecx = buffer; 0x08048772 <+178>: mov %ecx,(%esp) <-- 1st arg 0x08048775 <+181>: mov %eax,0x4(%esp) <-- 2nd arg, length. 0x08048779 <+185>: call 0x8048480 memfrob(buffer, length); // see the manpage of memfrob. // Type 'man memfrob' on your shell command line. 0x0804877e <+190>: lea -0x20c(%ebp),%ecx <-- 1st is from here, ebp_20c == buffer. 0x08048784 <+196>: mov %ecx,(%esp) <-- 1st arg 0x08048787 <+199>: mov %eax,-0x234(%ebp) <-- not optimized for eax... 0x0804878d <+205>: call 0x8048660 add_a_space(buffer); ### Let's decompile add_a_space function here.. gdb-peda$ disas add_a_space Dump of assembler code for function add_a_space: 0x08048660 <+0>: push %ebp 0x08048661 <+1>: mov %esp,%ebp 0x08048663 <+3>: sub $0x8,%esp 0x08048666 <+6>: mov 0x8(%ebp),%eax <-- 0x8(%ebp) is the caller's 1st argument of the function; eax stores the buffer pointer 0x08048669 <+9>: movl $0x0,-0x4(%ebp) <-- ebp_4 = 0 0x08048670 <+16>: mov %eax,-0x8(%ebp) <-- ebp_8 = buffer; 0x08048673 <+19>: mov 0x8(%ebp),%eax <- eax = buffer 0x08048676 <+22>: mov -0x4(%ebp),%ecx <-- ecx = ebp_4 0x08048679 <+25>: movsbl (%eax,%ecx,1),%eax <-- buffer[ecx] 0x0804867d <+29>: cmp $0x0,%eax <-- check if buffer[ecx] == 0 0x08048680 <+32>: je 0x80486b1 <-- jump to +81 if that value is zero, which is break; 0x08048686 <+38>: mov 0x8(%ebp),%eax <-- eax = buffer \ 0x08048689 <+41>: mov -0x4(%ebp),%ecx <-- ecx = ebp_4 | 0x0804868c <+44>: movsbl (%eax,%ecx,1),%eax <-- eax = buffer[ecx] | 0x08048690 <+48>: add $0x20,%eax <-- eax += 0x20 | 0x08048693 <+51>: and $0xff,%eax <-- eax &= 0xff |------- buffer[ebp_4] = (buffer[ebp_4]+0x20)&0xff; 0x08048698 <+56>: mov %al,%dl <-- dl = al; | 0x0804869a <+58>: mov 0x8(%ebp),%eax <-- eax = buffer | 0x0804869d <+61>: mov -0x4(%ebp),%ecx <-- ecx == ebp_4 | 0x080486a0 <+64>: mov %dl,(%eax,%ecx,1) <-- buffer[ecx] = dl / 0x080486a3 <+67>: mov -0x4(%ebp),%eax <-- eax == ebp_4 \ 0x080486a6 <+70>: add $0x1,%eax <-- eax += 1 |----- ebp_4 += 1; 0x080486a9 <+73>: mov %eax,-0x4(%ebp) <-- ebp_4 = eax / 0x080486ac <+76>: jmp 0x8048673 <-- jump back - loop 0x080486b1 <+81>: add $0x8,%esp 0x080486b4 <+84>: pop %ebp 0x080486b5 <+85>: ret End of assembler dump. So it is something like: void add_a_space() { int i = ebp_4; char *buffer = ebp_8; while(1) { if(buffer[i] == '\0') break; buffer[i] = (buffer[i]^0x20)&0xff; } } 0x08048792 <+210>: lea -0x20c(%ebp),%eax <-- 1st is from here, ebp_20c == buffer 0x08048798 <+216>: mov %eax,(%esp) <-- 1st arg 0x0804879b <+219>: call 0x80484b0 int int_value = atoi(buffer); 0x080487a0 <+224>: lea 0x80489bd,%ecx <-- 1st is from here 0x080487a6 <+230>: mov %eax,-0x214(%ebp) // ebp_214 = int_value; return value of atoi. 0x080487ac <+236>: mov -0x214(%ebp),%eax 0x080487b2 <+242>: mov %ecx,(%esp) <-- 1st arg 0x080487b5 <+245>: mov %eax,0x4(%esp) <-- 2nd arg, int_value. 0x080487b9 <+249>: call 0x8048410 gdb-peda$ x/s 0x80489bd 0x80489bd: "Your Integer: %u\n" printf("Your Integer: %u\n", int_value); 0x080487be <+254>: mov $0x14,%ecx ecx = 0x14; 0x080487c3 <+259>: lea 0x80489cf,%edx <-- 3rd is from here, do x/s, then its "%x" 0x080487c9 <+265>: lea -0x20c(%ebp),%esi <-- 1st is from here, buffer. 0x080487cf <+271>: mov -0x214(%ebp),%edi <-- 4th is from here, int_value. 0x080487d5 <+277>: mov %esi,(%esp) <-- 1st arg 0x080487d8 <+280>: movl $0x14,0x4(%esp) <-- 2nd arg 0x080487e0 <+288>: mov %edx,0x8(%esp) <-- 3rd arg 0x080487e4 <+292>: mov %edi,0xc(%esp) <-- 4th arg 0x080487e8 <+296>: mov %eax,-0x238(%ebp) <-- ret val of printf, ignore. 0x080487ee <+302>: mov %ecx,-0x23c(%ebp) 0x080487f4 <+308>: call 0x8048460 snprintf(buffer, 0x14, "%x", int_value); 0x080487f9 <+313>: lea 0x80489d2,%ecx <-- 1st is from here 0x080487ff <+319>: lea -0x20c(%ebp),%edx <-- 2nd is from here: buffer 0x08048805 <+325>: mov %ecx,(%esp) <-- 1st arg 0x08048808 <+328>: mov %edx,0x4(%esp) <-- 2nd arg 0x0804880c <+332>: mov %eax,-0x240(%ebp) <-- ret val, ignore... 0x08048812 <+338>: call 0x8048410 gdb-peda$ x/s 0x80489d2 0x80489d2: "Your Hex Integer: %s\n" printf("Your Hex Integer: %s\n", buffer); 0x08048817 <+343>: lea -0x20c(%ebp),%ecx <-- 1st is from here, buffer. 0x0804881d <+349>: mov %esp,%edx <-- now edx is new esp. 0x0804881f <+351>: mov %ecx,(%edx) <-- 1st arg 0x08048821 <+353>: mov %eax,-0x244(%ebp) <-- ignore ret val. 0x08048827 <+359>: mov %ecx,-0x248(%ebp) <-- ebp_248 = buffer. 0x0804882d <+365>: call 0x8048430 int length2 = strlen(buffer); 0x08048832 <+370>: mov %eax,-0x210(%ebp) <-- ebp_210 = length2. 0x08048838 <+376>: mov -0x210(%ebp),%eax <-- 2nd is from here, length2. 0x0804883e <+382>: mov -0x248(%ebp),%ecx <-- 1st is from here, ebp_248, see +359, it's buffer. 0x08048844 <+388>: mov %ecx,(%esp) <-- 1st arg 0x08048847 <+391>: mov %eax,0x4(%esp) <-- 2nd arg? 0x0804884b <+395>: call 0x8048480 memfrob(buffer, length2); 0x08048850 <+400>: lea -0x20c(%ebp),%ecx <-- 1st is buffer.. 0x08048856 <+406>: mov %ecx,(%esp) <-- 1st arg 0x08048859 <+409>: mov %eax,-0x24c(%ebp) <-- ret val, ignore 0x0804885f <+415>: call 0x8048660 add_a_space(buffer); 0x08048864 <+420>: lea -0x20c(%ebp),%eax // eax = buffer 0x0804886a <+426>: mov (%eax),%eax // eax = *buffer, read 4 byte integer value, because store to eax (a 4 byte register) 0x0804886c <+428>: sub $0x68333a3e,%eax // eax -= 0x68333a3e; 0x08048871 <+433>: setne %bl // if eax != 0, bl = 1; if eax == 0, bl = 0; 0x08048874 <+436>: movzbl %bl,%ecx // ecx = bl; 0x08048877 <+439>: cmp $0x0,%ecx 0x0804887a <+442>: mov %eax,-0x250(%ebp) setne sets when eax - 0x68333a3e != 0. To get into the if condition, we need to make that zero, i.e., the first four bytes of the buffer must be 0x68333a3e. In Intel architecture, they store integer value in little endian, so that will be stored as [0x3e, 0x3a, 0x33, 0x68]. And if you run the following python code, you can get the character value per each character code: >>> ''.join([chr(i) for i in [0x3e, 0x3a, 0x33, 0x68]]) '>:3h' So the first four byte of the buffer should be: >:3h if(eax == 0) { 0x08048880 <+448>: jne 0x80488a4 <-- +484 is the start of else branch. 0x08048886 <+454>: lea 0x80489f0,%eax 0x0804888c <+460>: mov %eax,(%esp) 0x0804888f <+463>: call 0x8048410 gdb-peda$ x/s 0x80489f0 0x80489f0: "Great! you got my password!\n" -> printf("Great! you got my password!\n"); 0x08048894 <+468>: mov %eax,-0x254(%ebp) 0x0804889a <+474>: call 0x80485f0 get_a_shell(); 0x0804889f <+479>: jmp 0x80488b8 <-- end if } else { 0x080488a4 <+484>: lea 0x8048a0d,%eax 0x080488aa <+490>: mov %eax,(%esp) 0x080488ad <+493>: call 0x8048410 gdb-peda$ x/s 0x8048a0d 0x8048a0d: "Oh no\n" -> printf("Oh no\n"); 0x080488b2 <+498>: mov %eax,-0x258(%ebp) } 0x080488b8 <+504>: add $0x25c,%esp 0x080488be <+510>: pop %esi 0x080488bf <+511>: pop %edi 0x080488c0 <+512>: pop %ebx 0x080488c1 <+513>: pop %ebp 0x080488c2 <+514>: ret Let's collect all the code. main() { printf("Let's start an esoteric game!\n"); password(); } void password() { char buffer[0x200]; // buffer is ebp_20c; char *ebp_218 = "%50s"; char *ebp_21c = buffer; // see +18 for %ecx, ecx is buffer. int ebp_220 = 0; // edx was zero by +24 int ebp_224 = 0x200; // see +26, esi = 0x200. memset(buffer, 0, 0x200); scanf("%50s", buffer); printf("Your buffer: %s\n", buffer); int length = strlen(buffer); memfrob(buffer, length); // see the manpage of memfrob. // Type 'man memfrob' on your shell command line. add_a_space(buffer); int int_value = atoi(buffer); printf("Your Integer: %u\n", int_value); snprintf(buffer, 0x14, "%x", int_value); printf("Your Hex Integer: %s\n", buffer); int length2 = strlen(buffer); memfrob(buffer, length2); add_a_space(buffer); if(buffer[0] == '>' && buffer[1] == ':' && buffer[2] == '3' && buffer[3] == 'h') { // buffer's first four bytes are: 0x68333a3e, which is // 0x3e, 0x3a, 0x33, 0x68 => '>:3h' printf("Great! you got my password!\n"); get_a_shell(); } else { printf("Oh no\n"); } } Do you know how to call get_a_shell() to get the flag?