=============== level5 tutorial =============== Let's run gdb first. $ gdb /home/labs/week1/level5/level5 Get the disassembly of the main function: pwndbg> disass main Dump of assembler code for function main: 0x08048880 <+0>: push %ebp 0x08048881 <+1>: mov %esp,%ebp 0x08048883 <+3>: sub $0x8,%esp 0x08048886 <+6>: lea 0x80489ad,%eax 0x0804888c <+12>: mov %eax,(%esp) 0x0804888f <+15>: call 0x80483f0 0x08048894 <+20>: mov %eax,-0x4(%ebp) 0x08048897 <+23>: call 0x8048600 0x0804889c <+28>: xor %eax,%eax 0x0804889e <+30>: add $0x8,%esp 0x080488a1 <+33>: pop %ebp 0x080488a2 <+34>: ret End of assembler dump. pwndbg> With a simple analysis, it calls printf with 0x08048886 <+6>: lea 0x80489ad,%eax 0x0804888c <+12>: mov %eax,(%esp) 0x0804888f <+15>: call 0x80483f0 printf(0x80489ad), and if we set a break point at main+15 and run, pwndbg> b *main+15 Breakpoint 1 at 0x804888f pwndbg> r Starting program: /home/labs/week1/level5/level5 ► 0x804888f calll printf@plt <0x80483f0> format: 0x80489ad ◂— "Let's start a more complex game!\n" vararg: 0xf7fb1000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0 It prints out the string, and calls password() 0x08048897 <+23>: call 0x8048600 Let's get the disassembly of password() (please scroll down a bit...) pwndbg> disass password Dump of assembler code for function password: 0x08048600 <+0>: push %ebp 0x08048601 <+1>: mov %esp,%ebp 0x08048603 <+3>: push %esi 0x08048604 <+4>: sub $0x264,%esp 0x0804860a <+10>: xor %eax,%eax 0x0804860c <+12>: mov $0x200,%ecx 0x08048611 <+17>: lea -0x204(%ebp),%edx 0x08048617 <+23>: mov %edx,(%esp) 0x0804861a <+26>: movl $0x0,0x4(%esp) 0x08048622 <+34>: movl $0x200,0x8(%esp) 0x0804862a <+42>: mov %eax,-0x224(%ebp) 0x08048630 <+48>: mov %ecx,-0x228(%ebp) 0x08048636 <+54>: call 0x8048430 0x0804863b <+59>: movl $0x0,-0x210(%ebp) 0x08048645 <+69>: cmpl $0x200,-0x210(%ebp) 0x0804864f <+79>: jae 0x80486eb 0x08048655 <+85>: xor %eax,%eax 0x08048657 <+87>: mov $0x1,%ecx 0x0804865c <+92>: lea -0x208(%ebp),%edx 0x08048662 <+98>: mov %edx,-0x20c(%ebp) 0x08048668 <+104>: mov -0x20c(%ebp),%edx 0x0804866e <+110>: movl $0x0,(%esp) 0x08048675 <+117>: mov %edx,0x4(%esp) 0x08048679 <+121>: movl $0x1,0x8(%esp) 0x08048681 <+129>: mov %eax,-0x22c(%ebp) 0x08048687 <+135>: mov %ecx,-0x230(%ebp) 0x0804868d <+141>: call 0x80483e0 0x08048692 <+146>: mov -0x20c(%ebp),%ecx 0x08048698 <+152>: movsbl (%ecx),%ecx 0x0804869b <+155>: cmp $0xa,%ecx 0x0804869e <+158>: mov %eax,-0x234(%ebp) 0x080486a4 <+164>: jne 0x80486bd 0x080486aa <+170>: mov -0x210(%ebp),%eax 0x080486b0 <+176>: movb $0x0,-0x204(%ebp,%eax,1) 0x080486b8 <+184>: jmp 0x80486eb 0x080486bd <+189>: mov -0x20c(%ebp),%eax 0x080486c3 <+195>: mov (%eax),%cl 0x080486c5 <+197>: mov -0x210(%ebp),%eax 0x080486cb <+203>: mov %cl,-0x204(%ebp,%eax,1) 0x080486d2 <+210>: jmp 0x80486d7 0x080486d7 <+215>: mov -0x210(%ebp),%eax 0x080486dd <+221>: add $0x1,%eax 0x080486e0 <+224>: mov %eax,-0x210(%ebp) 0x080486e6 <+230>: jmp 0x8048645 0x080486eb <+235>: movl $0x0,-0x218(%ebp) 0x080486f5 <+245>: cmpl $0x200,-0x218(%ebp) 0x080486ff <+255>: jae 0x8048746 0x08048705 <+261>: mov -0x218(%ebp),%eax 0x0804870b <+267>: movsbl -0x204(%ebp,%eax,1),%eax 0x08048713 <+275>: cmp $0x0,%eax 0x08048716 <+278>: jne 0x804872d 0x0804871c <+284>: mov -0x218(%ebp),%eax 0x08048722 <+290>: mov %eax,-0x214(%ebp) 0x08048728 <+296>: jmp 0x8048746 0x0804872d <+301>: jmp 0x8048732 0x08048732 <+306>: mov -0x218(%ebp),%eax 0x08048738 <+312>: add $0x1,%eax 0x0804873b <+315>: mov %eax,-0x218(%ebp) 0x08048741 <+321>: jmp 0x80486f5 0x08048746 <+326>: cmpl $0x7,-0x214(%ebp) 0x0804874d <+333>: je 0x804877e 0x08048753 <+339>: lea 0x8048957,%eax 0x08048759 <+345>: mov %eax,(%esp) 0x0804875c <+348>: call 0x80483f0 0x08048761 <+353>: mov $0xffffffff,%ecx 0x08048766 <+358>: movl $0xffffffff,(%esp) 0x0804876d <+365>: mov %eax,-0x238(%ebp) 0x08048773 <+371>: mov %ecx,-0x23c(%ebp) 0x08048779 <+377>: call 0x8048410 0x0804877e <+382>: lea -0x204(%ebp),%eax 0x08048784 <+388>: mov %eax,(%esp) 0x08048787 <+391>: call 0x8048470 0x0804878c <+396>: mov $0x14,%ecx 0x08048791 <+401>: lea 0x804896d,%edx 0x08048797 <+407>: lea -0x204(%ebp),%esi 0x0804879d <+413>: mov %eax,-0x21c(%ebp) 0x080487a3 <+419>: mov -0x21c(%ebp),%eax 0x080487a9 <+425>: mov %esi,(%esp) 0x080487ac <+428>: movl $0x14,0x4(%esp) 0x080487b4 <+436>: mov %edx,0x8(%esp) 0x080487b8 <+440>: mov %eax,0xc(%esp) 0x080487bc <+444>: mov %ecx,-0x240(%ebp) 0x080487c2 <+450>: call 0x8048440 0x080487c7 <+455>: movl $0x0,-0x220(%ebp) 0x080487d1 <+465>: mov %eax,-0x244(%ebp) 0x080487d7 <+471>: cmpl $0xa,-0x220(%ebp) 0x080487de <+478>: jge 0x804884f 0x080487e4 <+484>: mov -0x220(%ebp),%eax 0x080487ea <+490>: movsbl -0x204(%ebp,%eax,1),%eax 0x080487f2 <+498>: xor $0x1,%eax 0x080487f5 <+501>: mov -0x220(%ebp),%ecx 0x080487fb <+507>: movsbl 0x804a03c(,%ecx,1),%ecx 0x08048803 <+515>: cmp %ecx,%eax 0x08048805 <+517>: je 0x8048836 0x0804880b <+523>: lea 0x8048970,%eax 0x08048811 <+529>: mov %eax,(%esp) 0x08048814 <+532>: call 0x80483f0 0x08048819 <+537>: mov $0xffffffff,%ecx 0x0804881e <+542>: movl $0xffffffff,(%esp) 0x08048825 <+549>: mov %eax,-0x248(%ebp) 0x0804882b <+555>: mov %ecx,-0x24c(%ebp) 0x08048831 <+561>: call 0x8048410 0x08048836 <+566>: jmp 0x804883b 0x0804883b <+571>: mov -0x220(%ebp),%eax 0x08048841 <+577>: add $0x1,%eax 0x08048844 <+580>: mov %eax,-0x220(%ebp) 0x0804884a <+586>: jmp 0x80487d7 0x0804884f <+591>: lea 0x8048990,%eax 0x08048855 <+597>: mov %eax,(%esp) 0x08048858 <+600>: call 0x80483f0 0x0804885d <+605>: mov %eax,-0x250(%ebp) 0x08048863 <+611>: call 0x8048590 0x08048868 <+616>: add $0x264,%esp 0x0804886e <+622>: pop %esi 0x0804886f <+623>: pop %ebp 0x08048870 <+624>: ret End of assembler dump. pwndbg> hmm... it's a long function, but don't worry, let's analyze this step-by-step. => 0x08048600 <+0>: push %ebp 0x08048601 <+1>: mov %esp,%ebp 0x08048603 <+3>: push %esi 0x08048604 <+4>: sub $0x264,%esp 0x0804860a <+10>: xor %eax,%eax 0x0804860c <+12>: mov $0x200,%ecx 0x08048611 <+17>: lea -0x204(%ebp),%edx 0x08048617 <+23>: mov %edx,(%esp) 0x0804861a <+26>: movl $0x0,0x4(%esp) 0x08048622 <+34>: movl $0x200,0x8(%esp) 0x0804862a <+42>: mov %eax,-0x224(%ebp) 0x08048630 <+48>: mov %ecx,-0x228(%ebp) 0x08048636 <+54>: call 0x8048430 Let's analyze the first call, memset() first, for its arguments. All the arguments passing is done over positive indexing over %esp. (%esp) stores the 1st argument, 0x4(%esp) stores the 2nd argument, 0x8(%esp) stores the 3rd argument, and 0xc(%esp) stores the 4th argument. 0x08048611 <+17>: lea -0x204(%ebp),%edx 0x08048617 <+23>: mov %edx,(%esp) <-- 1st arg, edx comes from the address of -0x204(%ebp) 0x0804861a <+26>: movl $0x0,0x4(%esp) <-- 2nd arg, 0 0x08048622 <+34>: movl $0x200,0x8(%esp) <-- 3rd arg, 0x200 0x0804862a <+42>: mov %eax,-0x224(%ebp) 0x08048630 <+48>: mov %ecx,-0x228(%ebp) 0x08048636 <+54>: call 0x8048430 So it calls: memset(ebp_204, 0, 0x200); For the 1st argument, because lea is for moving an 'address' and its a local variable (negative indexing over %ebp means local variable), we may regard ebp_204 as a buffer (as memset expects a buffer). Then, what's the size? 0x200. Why? memset(buffer, initialize_value, size).. Let's write it like the following: char buffer[0x200]; // ebp_204 memset(buffer, 0, 0x200); Let's move on to the next; At password+69, you may identify cmpl, and jae (jump if above or equal). Whenever you see cmp or test followed by jae or some other jumps, you may regard this as one of 'if', 'while', or 'for'. Let's check what it is. 0x0804863b <+59>: movl $0x0,-0x210(%ebp) 0x08048645 <+69>: cmpl $0x200,-0x210(%ebp) <-------------- 0x0804864f <+79>: jae 0x80486eb | | This jumps to password+235, and if you check lines around password+235, | JUMP | BACK 0x080486e6 <+230>: jmp 0x8048645 | 0x080486eb <+235>: movl $0x0,-0x218(%ebp) | | It jumps back to password+69.... ----------------- So, it's either while or for. And, it first set ebp_210 = 0, and compare 0x200 to ebp_210. To detect if it is 'for' or 'while', let's check more instructions around password+235... 0x080486d7 <+215>: mov -0x210(%ebp),%eax <-- 0x080486dd <+221>: add $0x1,%eax <-- 0x080486e0 <+224>: mov %eax,-0x210(%ebp) <-- incrementing loop variable, ebp_210 += 1; 0x080486e6 <+230>: jmp 0x8048645 0x080486eb <+235>: movl $0x0,-0x218(%ebp) Can you see ebp_210? Yes, it does ebp_210 += 1; Then it is something like: 1) It is a loop (jmp to +235, the line before +235 gets back to +69..) 2) It sets a loop variable (ebp_210) as 0 at start, compare against 0x200. 3) At the end of the loop (before reaching to +235), it increments the loop variable by 1(ebp_210). And, we write such a loop as 'for'. If we set ebp_210 as 'int i', then it could be written as: for (int i=0; i<0x200; ++i) { // exit loop if i >= 0x200 because // cmp $0x200, -0x210(%ebp) and JAE, i >= 0x200, // so inside the loop, it must be i < 0x200. // Let's put assembly after +79 and before +235 in here. 0x08048655 <+85>: xor %eax,%eax // This instruction, sets eax as 0 (xor-ing the same values will always result in 0). 0x08048657 <+87>: mov $0x1,%ecx // %ecx is 1 0x0804865c <+92>: lea -0x208(%ebp),%edx // edx stores the address of local variable ebp_208 0x08048662 <+98>: mov %edx,-0x20c(%ebp) // move that to a local variable ebp_20c = &ebp_208 0x08048668 <+104>: mov -0x20c(%ebp),%edx // now edx stores the address of ebp_208 0x0804866e <+110>: movl $0x0,(%esp) <-- 1st arg (0) 0x08048675 <+117>: mov %edx,0x4(%esp) <-- 2nd arg (&ebp_208) 0x08048679 <+121>: movl $0x1,0x8(%esp) <-- 3rd arg (1) 0x08048681 <+129>: mov %eax,-0x22c(%ebp) 0x08048687 <+135>: mov %ecx,-0x230(%ebp) 0x0804868d <+141>: call 0x80483e0 // a function call read. analyze args from (%esp)... //read(0, &ebp_208, 1); // what it does is, reading 1 byte from the file descriptor (fd) 0. // In UNIX systems, fd 0 is for stdin (console input), // fd 1 is for stdout (console output), // fd 2 is for stderr (console error output). // So it reads 1 byte from console input, and store that to ebp_208. // And now we know that ebp_208 is a variable that stores one byte, // and we may safely assume that that's: char c; // ebp_208 // and we may re-write the read function as: read(0, &c, 1); 0x08048692 <+146>: mov -0x20c(%ebp),%ecx // ecx will store the &ebp_208, &c. 0x08048698 <+152>: movsbl (%ecx),%ecx // this, movsbl, will read one byte by dereferencing ecx, which is // the address of the character c (&c), and store that to ecx. // so we may regard ecx as the character c. // Next is, cmp and jne combo. Let's detect what it is (if, for, while). 0x0804869b <+155>: cmp $0xa,%ecx 0x0804869e <+158>: mov %eax,-0x234(%ebp) 0x080486a4 <+164>: jne 0x80486bd // Let's check +189, and it is jump forward (to +235). // In such a case (not a jump back), you may regard this as 'if'. // So it compares the character 'c' (ecx) to 0xa, // which is a newline character ('\n'), and if it is not, jumps to +189, // and if it is, continue to execute next. // Then, let's write if condition with jne (jump not equal to 0xa) if (c != '\n') { 0x080486aa <+170>: mov -0x210(%ebp),%eax 0x080486b0 <+176>: movb $0x0,-0x204(%ebp,%eax,1) // ebp_210 is i, and now eax = i; // move 0 to ebp_204[eax * 1] == ebp_204[eax] == buffer[eax] (because ebp_204 is buffer) // then, it is, putting the null character at the current end of the buffer. buffer[i] = '\0'; 0x080486b8 <+184>: jmp 0x80486eb // jump forward, +235 // and the next is jump to +235, which is the end of the loop.. so loop exit.. // then, we may regard that as break. break; } else { 0x080486bd <+189>: mov -0x20c(%ebp),%eax // 189 is here.. // ebp_20c stores the address of ebp_208, which is &c. // eax holds the address of c (&c) 0x080486c3 <+195>: mov (%eax),%cl // read the address of c (the value itself) and move that to %cl. // now, ecx holds the value c. 0x080486c5 <+197>: mov -0x210(%ebp),%eax // ebp_210 is i, so eax == i; 0x080486cb <+203>: mov %cl,-0x204(%ebp,%eax,1) // then, this will do // ebp_204[eax * 1] = %cl, which is buffer[eax] = c, // and it is: buffer[i] = c; 0x080486d2 <+210>: jmp 0x80486d7 // you may ignore this line as it jumps to the next line, 215 0x080486d7 <+215>: mov -0x210(%ebp),%eax 0x080486dd <+221>: add $0x1,%eax 0x080486e0 <+224>: mov %eax,-0x210(%ebp) // and these three lines are about: // ebp_210 += 1, // and it is: // i += 1, and it is already included in the for loop. 0x080486e6 <+230>: jmp 0x8048645 // and it jumps back to the for loop. } } Let's clean up the decompiled code: char buffer[0x200]; // ebp_204 memset(buffer, 0, 0x200); for (int i=0; i<0x200; ++i) { char c; // ebp_208 read(0, &c, 1); if (c != '\n') { buffer[i] = '\0'; break; } else { buffer[i] = c; } } What does it do? It gets each bytes, and exit the loop if it gets the newline. Yes, it is equivalent to: gets(buffer); Now you have decompiled up to +235. Let's move on to the next. 0x080486eb <+235>: movl $0x0,-0x218(%ebp) 0x080486f5 <+245>: cmpl $0x200,-0x218(%ebp) 0x080486ff <+255>: jae 0x8048746 We have seen this pattern. mov, cmp, jae... yes, possibly it iss a loop. To determine if it is a loop or not, let's check the jump target, +326. 0x08048732 <+306>: mov -0x218(%ebp),%eax 0x08048738 <+312>: add $0x1,%eax 0x0804873b <+315>: mov %eax,-0x218(%ebp) 0x08048741 <+321>: jmp 0x80486f5 0x08048746 <+326>: cmpl $0x7,-0x214(%ebp) Can you see: ebp_218 += 1; and jump back to +245? Yes, it is a loop, loop variable as ebp_218, set it as 0, and runs upto 0x200. Let's write it as: // and put all assembly upto 306 in here.. for (int j=0; j<0x200; ++j) { // loop variable j is ebp_218 0x08048705 <+261>: mov -0x218(%ebp),%eax // eax = j; 0x0804870b <+267>: movsbl -0x204(%ebp,%eax,1),%eax // this is, eax = ebp_204[eax * 1] // eax = ebp_204[j] // eax = buffer[j]; 0x08048713 <+275>: cmp $0x0,%eax // compare 0 with buffer[j] 0x08048716 <+278>: jne 0x804872d // jump not equal, but there is no jump back at 301 (jumps to +306, forward) // so it is not a loop. // then let's write this cmp+jne as 'if'. if (buffer[j] != 0) { 0x0804872d <+301>: jmp 0x8048732 // this line does nothing.. 0x08048732 <+306>: mov -0x218(%ebp),%eax 0x08048738 <+312>: add $0x1,%eax 0x0804873b <+315>: mov %eax,-0x218(%ebp) // this is j += 1; and is already included in the for loop.. 0x08048741 <+321>: jmp 0x80486f5 // jump back to the loop... } else { 0x0804871c <+284>: mov -0x218(%ebp),%eax // eax = j; 0x08048722 <+290>: mov %eax,-0x214(%ebp) // ebp_214 = j; 0x08048728 <+296>: jmp 0x8048746 // jump out the loop, so break; break; } } Let's clean up the decompiled code: char buffer[0x200]; // ebp_204 memset(buffer, 0, 0x200); for (int i=0; i<0x200; ++i) { char c; // ebp_208 read(0, &c, 1); if (c != '\n') { buffer[i] = '\0'; // put zero at the end of your input, instead of the newline. break; } else { buffer[i] = c; } } // up to this point, the program does gets(buffer). for (int j=0; j<0x200; ++j) { // loop variable j is ebp_218 if (buffer[j] != 0) { } else { ebp_214 = j; break; } } What does this code do? In C, all the string ends with '\0', a NULL character, value zero. So from your input, it searches for '0', which is the end of the string. See the code above about putting '\0', at the end of the string (instead of the newline input). So it increases 'j' until the end of the string, and put that value to ebp_214. Then what value does ebp_214 stores? yes, it stores the length of your input. We may change: ebp_214 = j; as length = j; Let's move on to next. 0x08048746 <+326>: cmpl $0x7,-0x214(%ebp) 0x0804874d <+333>: je 0x804877e cmpl, je, it seems one of if, for, while, let's check asms around +382: 0x08048766 <+358>: movl $0xffffffff,(%esp) 0x0804876d <+365>: mov %eax,-0x238(%ebp) 0x08048773 <+371>: mov %ecx,-0x23c(%ebp) 0x08048779 <+377>: call 0x8048410 0x0804877e <+382>: lea -0x204(%ebp),%eax There is no jump back. Then it is if, and ebp_214 is the length of your input. 0x08048746 <+326>: cmpl $0x7,-0x214(%ebp) 0x0804874d <+333>: je 0x804877e if (length != 7) { // because it jumps to +382, if we set the inverse of the condition, not equal to 7, 0x08048753 <+339>: lea 0x8048957,%eax 0x08048759 <+345>: mov %eax,(%esp) <-- 1st arg, 0x8048957 0x0804875c <+348>: call 0x80483f0 // calls printf with 0x8048957 printf(0x8048957); 0x08048761 <+353>: mov $0xffffffff,%ecx 0x08048766 <+358>: movl $0xffffffff,(%esp) <-- 1st arg 0x0804876d <+365>: mov %eax,-0x238(%ebp) 0x08048773 <+371>: mov %ecx,-0x23c(%ebp) 0x08048779 <+377>: call 0x8048410 // calls exit. exit(-1); // 0xffffffff is -1. } Let's clean up the code. char buffer[0x200]; // ebp_204 memset(buffer, 0, 0x200); for (int i=0; i<0x200; ++i) { char c; // ebp_208 read(0, &c, 1); if (c != '\n') { buffer[i] = '\0'; // put zero at the end of your input, instead of the newline. break; } else { buffer[i] = c; } } // up to this point, the program does gets(buffer). for (int j=0; j<0x200; ++j) { // loop variable j is ebp_218 if (buffer[j] != 0) { } else { ebp_214 = j; break; } } if (length != 7) { // because it jumps to +382, if we set the inverse of the condition, not equal to 7, printf(0x8048957); exit(-1); // 0xffffffff is -1. } Let's move on to next. 0x0804877e <+382>: lea -0x204(%ebp),%eax 0x08048784 <+388>: mov %eax,(%esp) <-- 1st arg, ebp_204. 0x08048787 <+391>: call 0x8048470 // calls atoi. // ebp_204 is buffer. integer_value = atoi(buffer); // the return value of the function is stored into eax. // move on to the next function call... 0x0804878c <+396>: mov $0x14,%ecx 0x08048791 <+401>: lea 0x804896d,%edx 0x08048797 <+407>: lea -0x204(%ebp),%esi 0x0804879d <+413>: mov %eax,-0x21c(%ebp) 0x080487a3 <+419>: mov -0x21c(%ebp),%eax 0x080487a9 <+425>: mov %esi,(%esp) <-- 1st arg esi, (ebp_204, buffer) 0x080487ac <+428>: movl $0x14,0x4(%esp) <-- 2nd arg (0x14) 0x080487b4 <+436>: mov %edx,0x8(%esp) <-- 3rd arg (edx, 0x804896d) 0x080487b8 <+440>: mov %eax,0xc(%esp) <-- 4th arg (ebp_21c, from eax, then the return value of atoi..) 0x080487bc <+444>: mov %ecx,-0x240(%ebp) 0x080487c2 <+450>: call 0x8048440 // It calls snprintf(char *, size_t, char *, ...) snprintf(buffer, 0x14, 0x804896d, integer_value); Let's clean up the code again. char buffer[0x200]; // ebp_204 memset(buffer, 0, 0x200); for (int i=0; i<0x200; ++i) { char c; // ebp_208 read(0, &c, 1); if (c != '\n') { buffer[i] = '\0'; // put zero at the end of your input, instead of the newline. break; } else { buffer[i] = c; } } // up to this point, the program does gets(buffer). for (int j=0; j<0x200; ++j) { // loop variable j is ebp_218 if (buffer[j] != 0) { } else { ebp_214 = j; break; } } if (length != 7) { // because it jumps to +382, if we set the inverse of the condition, not equal to 7, printf(0x8048957); exit(-1); // 0xffffffff is -1. } integer_value = atoi(buffer); // the return value of the function is stored into eax. snprintf(buffer, 0x14, 0x804896d, integer_value); 0x080487c7 <+455>: movl $0x0,-0x220(%ebp) 0x080487d1 <+465>: mov %eax,-0x244(%ebp) 0x080487d7 <+471>: cmpl $0xa,-0x220(%ebp) 0x080487de <+478>: jge 0x804884f // mov, cmpl, jge.. seems like a loop. Let's check +591. 0x0804883b <+571>: mov -0x220(%ebp),%eax 0x08048841 <+577>: add $0x1,%eax 0x08048844 <+580>: mov %eax,-0x220(%ebp) 0x0804884a <+586>: jmp 0x80487d7 0x0804884f <+591>: lea 0x8048990,%eax // Yes, it uses ebp_220 as a loop variable, having ebp_220 += 1 around +571, // and it jumps back to +471 at +586... // A for loop! for (int k=0; k<10; ++k) { // ebp_220 as k, set as 0 (+455), compare with 10 (+471, 0xa is 10). 0x080487e4 <+484>: mov -0x220(%ebp),%eax // eax = k; 0x080487ea <+490>: movsbl -0x204(%ebp,%eax,1),%eax // eax = ebp_204[eax * 1]; // eax = buffer[k]; 0x080487f2 <+498>: xor $0x1,%eax // eax = buffer[k] ^ 1; 0x080487f5 <+501>: mov -0x220(%ebp),%ecx // ecx = k; 0x080487fb <+507>: movsbl 0x804a03c(,%ecx,1),%ecx // ecx = 0x804a03c[ecx * 1]; // ecx = 0x804a03c[k]; 0x08048803 <+515>: cmp %ecx,%eax 0x08048805 <+517>: je 0x8048836 // this is if, because +566 is jump forward (loop if jump back) // we will negate the condition (je, so will use not equal to put next assmebly lines inside if) if ( (buffer[k]^1) == 0x804a03c[k] ) { 0x0804880b <+523>: lea 0x8048970,%eax 0x08048811 <+529>: mov %eax,(%esp) 0x08048814 <+532>: call 0x80483f0 printf(0x8048970); 0x08048819 <+537>: mov $0xffffffff,%ecx 0x0804881e <+542>: movl $0xffffffff,(%esp) 0x08048825 <+549>: mov %eax,-0x248(%ebp) 0x0804882b <+555>: mov %ecx,-0x24c(%ebp) 0x08048831 <+561>: call 0x8048410 exit(-1); } else { 0x08048836 <+566>: jmp 0x804883b // ignore this, jumps to the next instruction. 0x0804883b <+571>: mov -0x220(%ebp),%eax 0x08048841 <+577>: add $0x1,%eax 0x08048844 <+580>: mov %eax,-0x220(%ebp) 0x0804884a <+586>: jmp 0x80487d7 // this is // k += 1 and jump back to the loop, already handled by the for loop.. } } // After finishing the loop without exit(), 0x0804884f <+591>: lea 0x8048990,%eax 0x08048855 <+597>: mov %eax,(%esp) 0x08048858 <+600>: call 0x80483f0 // calls printf with 0x8048990 printf(0x8048990); 0x0804885d <+605>: mov %eax,-0x250(%ebp) 0x08048863 <+611>: call 0x8048590 // calls get_a_shell(); get_a_shell(); Let's collect the final code. And we will convert all string addresses by using x/s [address] in gdb: pwndbg> x/s 0x8048957 0x8048957: "Wrong string length!\n" pwndbg> x/s 0x8048970 0x8048970: "Wrong password, access denied.\n" pwndbg> x/s 0x804896d 0x804896d: "%u" pwndbg> x/s 0x8048990 0x8048990: "Great! you got my password!\n" pwndbg> x/s 0x804a03c 0x804a03c : "5385876380" char buffer[0x200]; // ebp_204 memset(buffer, 0, 0x200); for (int i=0; i<0x200; ++i) { char c; // ebp_208 read(0, &c, 1); if (c != '\n') { buffer[i] = '\0'; // put zero at the end of your input, instead of the newline. break; } else { buffer[i] = c; } } // up to this point, the program does gets(buffer). for (int j=0; j<0x200; ++j) { // loop variable j is ebp_218 if (buffer[j] != 0) { } else { ebp_214 = j; break; } } if (length != 7) { // because it jumps to +382, if we set the inverse of the condition, not equal to 7, printf("Wrong string length!\n"); exit(-1); // 0xffffffff is -1. } integer_value = atoi(buffer); // the return value of the function is stored into eax. snprintf(buffer, 0x14, "%u", integer_value); char *password_string = "5385876380"; for (int k=0; k<10; ++k) { // ebp_220 as k, set as 0 (+455), compare with 10 (+471, 0xa is 10). if ( (buffer[k]^1) == password_string[k] ) { printf("Wrong password, access denied.\n"); exit(-1); } else { } } printf("Great! you got my password!\n"); get_a_shell(); _ | | ___ __ _ _ __ _ _ ___ _ _ ___ ___ | |_ _____ / __/ _` | '_ \ | | | |/ _ \| | | | / __|/ _ \| \ \ / / _ \ | (_| (_| | | | | | |_| | (_) | |_| | \__ \ (_) | |\ V / __/ \___\__,_|_| |_| \__, |\___/ \__,_| |___/\___/|_| \_/ \___| __/ | |___/ _ _ _ _ _ ___ | | | | | | | | | |__ \ | |_| |__ ___ ___| |__ __ _| | | ___ _ __ __ _ ___ ) | | __| '_ \ / _ \ / __| '_ \ / _` | | |/ _ \ '_ \ / _` |/ _ \/ / | |_| | | | __/ | (__| | | | (_| | | | __/ | | | (_| | __/_| \__|_| |_|\___| \___|_| |_|\__,_|_|_|\___|_| |_|\__, |\___(_) __/ | |___/